@salesforce/afv-skills 1.53.0 → 1.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/dx-org-analyze/README.md +310 -0
- package/skills/dx-org-analyze/SKILL.md +261 -0
- package/skills/dx-org-analyze/references/collection-details.md +88 -0
- package/skills/dx-org-analyze/references/report-format.md +67 -0
- package/skills/dx-org-analyze/scripts/collect_org_data.py +823 -0
- package/skills/dx-org-analyze/scripts/compute_diff.py +1103 -0
- package/skills/dx-org-analyze/scripts/introspect_org.py +436 -0
- package/skills/dx-org-analyze/tests/README.md +46 -0
- package/skills/dx-org-analyze/tests/fixtures/org_a.json +76 -0
- package/skills/dx-org-analyze/tests/fixtures/org_b.json +70 -0
- package/skills/dx-org-analyze/tests/test_compute_diff.sh +284 -0
- package/skills/dx-org-analyze/tests/test_consistency.sh +428 -0
- package/skills/experience-design-validate/SKILL.md +163 -0
- package/skills/experience-design-validate/references/ai.md +54 -0
- package/skills/experience-design-validate/references/components.md +61 -0
- package/skills/experience-design-validate/references/craft.md +158 -0
- package/skills/experience-design-validate/references/data.md +59 -0
- package/skills/experience-design-validate/references/forms-flows.md +57 -0
- package/skills/experience-design-validate/references/interaction.md +53 -0
- package/skills/experience-design-validate/references/navigation.md +51 -0
- package/skills/experience-design-validate/references/performance.md +56 -0
- package/skills/experience-design-validate/references/records.md +60 -0
- package/skills/experience-design-validate/references/responsive.md +48 -0
- package/skills/experience-design-validate/references/scoring-rubric.md +257 -0
- package/skills/experience-design-validate/references/state.md +53 -0
- package/skills/experience-design-validate/references/trust.md +47 -0
- package/skills/experience-design-validate/references/usability.md +35 -0
- package/skills/experience-design-validate/references/visual-system.md +183 -0
- package/skills/service-agentforce-contact-center-coordinate/SKILL.md +170 -0
- package/skills/service-agentforce-contact-center-coordinate/assets/escalation-flow.flow-meta.xml +72 -0
- package/skills/service-agentforce-contact-center-coordinate/assets/omni-flow.flow-meta.xml +85 -0
- package/skills/service-agentforce-contact-center-coordinate/assets/report-template.md +52 -0
- package/skills/service-agentforce-contact-center-coordinate/references/agentforce-prerequisite.md +31 -0
- package/skills/service-agentforce-contact-center-coordinate/references/messaging_channel.md +76 -0
- package/skills/service-agentforce-contact-center-coordinate/references/number_management_api.md +78 -0
- package/skills/service-agentforce-contact-center-coordinate/references/omni-flow-routing.md +74 -0
- package/skills/service-agentforce-contact-center-coordinate/references/setup_summary.md +36 -0
- package/skills/service-agentforce-contact-center-coordinate/references/verification_and_errors.md +42 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/check-agentforce-prereq.sh +54 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/create-routing-flows.sh +49 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/create-voice-agent.sh +73 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/create-voice-channel.sh +66 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/fetch-numbers.sh +21 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/lib.sh +46 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/prepare-agent-workdir.sh +21 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/procure-number.sh +24 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/resolve-acc-queue.sh +27 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/resolve-channel-line.sh +37 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/resolve-flow-definition.sh +33 -0
- package/skills/service-agentforce-contact-center-coordinate/scripts/verify-number-live.sh +63 -0
package/package.json
CHANGED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# dx-org-analyze
|
|
2
|
+
|
|
3
|
+
Compare two Salesforce orgs and produce a comprehensive comparison report covering metadata, permissions, org settings, limits, profiles, installed packages, licenses, system permissions, and content-level code differences. Also supports single-org analysis via introspect mode.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- **Salesforce CLI** (`sf`) installed and authenticated to both orgs
|
|
8
|
+
- Admin-level access on both orgs (needed for Tooling API queries)
|
|
9
|
+
- Both orgs authenticated via `sf org login web` or `sf org login jwt`
|
|
10
|
+
- Python 3.10+
|
|
11
|
+
|
|
12
|
+
## Getting Started
|
|
13
|
+
|
|
14
|
+
### 1. Authenticate your orgs
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Authenticate via browser (interactive — most common)
|
|
18
|
+
sf org login web --alias my-dev-org
|
|
19
|
+
sf org login web --alias my-prod-org
|
|
20
|
+
|
|
21
|
+
# Via JWT for CI/automation
|
|
22
|
+
sf org login jwt --alias my-org --client-id <ID> --jwt-key-file <key> --username <user>
|
|
23
|
+
|
|
24
|
+
# Via access token (for orgs that don't support the standard connected app)
|
|
25
|
+
sf org login access-token --instance-url https://myorg.my.salesforce.com --alias my-org
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. Verify connectivity
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
sf org list # Shows all authenticated orgs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 3. Run a comparison (two orgs)
|
|
35
|
+
|
|
36
|
+
Invoke the skill and follow the prompts — it will show your authenticated orgs and ask you to pick a source and target.
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
Skill: dx-org-analyze
|
|
40
|
+
Request: "Compare my sandbox against production"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 4. Run an analysis (single org)
|
|
44
|
+
|
|
45
|
+
For a single-org analysis, invoke the skill with one org. It will collect data and produce an Org Analysis Report covering metadata inventory, org settings, limits, packages, licenses, system permissions, and deep data.
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
Skill: dx-org-analyze
|
|
49
|
+
Request: "Introspect my production org"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## How It Works
|
|
53
|
+
|
|
54
|
+
### Comparison (two orgs)
|
|
55
|
+
|
|
56
|
+
1. **List authenticated orgs** — Shows orgs from `sf org list`
|
|
57
|
+
2. **Select source and target** — Source is the reference; target is compared against it
|
|
58
|
+
3. **Collect data** — Queries metadata via SF CLI and Tooling API, org settings, limits, packages, licenses, system permissions, and deep data (per org, in parallel)
|
|
59
|
+
4. **Compute differences** — Set comparisons, field-level diffs, body-hash diffs, CRUD permission diffs, metadata enrichment with content-level differences
|
|
60
|
+
5. **Generate Org Comparison Report** — Drift Score → Summary Statistics → Detailed Differences → Package Components → Permissions → Org Values & Limits → Licenses → Deep Data
|
|
61
|
+
|
|
62
|
+
### Analysis (single org)
|
|
63
|
+
|
|
64
|
+
1. **Validate connectivity** — Confirms the org is reachable
|
|
65
|
+
2. **Collect data** — Same collection as comparison mode, but for one org
|
|
66
|
+
3. **Generate Org Analysis Report** — Metadata Inventory → Installed Packages → Org Settings → Org Limits → Licenses → System Permissions → Deep Data
|
|
67
|
+
|
|
68
|
+
## Report Structure
|
|
69
|
+
|
|
70
|
+
### Org Comparison Report (two orgs)
|
|
71
|
+
|
|
72
|
+
1. **Drift Score** — Overall weighted score (0–100%) with severity level
|
|
73
|
+
2. **Summary Statistics** — Counts per metadata type (with Identical and Different columns enriched from deep data)
|
|
74
|
+
3. **Metadata Components by Type** — Full breakdown of only-in-A, only-in-B, shared per type
|
|
75
|
+
4. **Profiles** — Shared, source-only, target-only profiles
|
|
76
|
+
5. **Installed Packages** — Version comparison, only-in-A, only-in-B
|
|
77
|
+
6. **Package Components** — Namespaced components grouped by namespace; non-namespaced packages flagged separately
|
|
78
|
+
7. **Org Permissions** — Boolean enabled/disabled diffs by category, plus non-boolean value diffs
|
|
79
|
+
8. **System Permissions** — PermissionsXxx fields on PermissionSet, per-set diffs
|
|
80
|
+
9. **Org Values & Limits** — Grouped by Identity, Storage, API, Feature Limits, Other
|
|
81
|
+
10. **Licenses** — User licenses, permission set licenses, package licenses with quantity diffs
|
|
82
|
+
11. **Deep Data** — Content-level diffs for Apex, Flows, Validation Rules, Custom Fields, etc.
|
|
83
|
+
|
|
84
|
+
### Org Analysis Report (single org)
|
|
85
|
+
|
|
86
|
+
1. **Summary** — Org identity, edition, metadata type and component counts
|
|
87
|
+
2. **Metadata Inventory** — Component counts per metadata type, org-native vs namespaced
|
|
88
|
+
3. **Installed Packages** — Package name, namespace, version
|
|
89
|
+
4. **Org Settings** — Enabled/disabled/non-boolean settings grouped by category
|
|
90
|
+
5. **Org Limits** — API, storage, feature limits grouped by category
|
|
91
|
+
6. **Licenses** — User licenses, permission set licenses, package licenses with usage counts
|
|
92
|
+
7. **System Permissions** — Permission sets and their enabled PermissionsXxx fields
|
|
93
|
+
8. **Deep Data** — Record counts for Apex classes, flows, custom fields, validation rules, etc.
|
|
94
|
+
9. **Collection Warnings** — Any non-fatal issues encountered during data collection
|
|
95
|
+
|
|
96
|
+
### Drift Score Formula
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
Overall = 60% × Metadata Drift + 30% × Permission Drift + 10% × Profile Drift
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Range | Level | Meaning |
|
|
103
|
+
|-------|-------|---------|
|
|
104
|
+
| 0–10% | LOW | Nearly identical orgs |
|
|
105
|
+
| 11–30% | MODERATE | Some features differ |
|
|
106
|
+
| 31–60% | HIGH | Significant divergence |
|
|
107
|
+
| 61–100% | CRITICAL | Fundamentally different orgs |
|
|
108
|
+
|
|
109
|
+
## What's Compared
|
|
110
|
+
|
|
111
|
+
### Metadata (Name-Level)
|
|
112
|
+
|
|
113
|
+
All types discovered via `sf org list metadata-types` (typically 100+ types).
|
|
114
|
+
Falls back to Tooling API queries if CLI metadata commands are unavailable.
|
|
115
|
+
Namespaced (managed-package) components are excluded from the summary table and listed separately under Package Components.
|
|
116
|
+
|
|
117
|
+
### Deep Data (Content-Level via Tooling API)
|
|
118
|
+
|
|
119
|
+
| Category | Comparison Depth |
|
|
120
|
+
|----------|-----------------|
|
|
121
|
+
| Apex Classes | Body hash, API version, LOC, status for shared classes |
|
|
122
|
+
| Apex Triggers | Body hash comparison for shared triggers |
|
|
123
|
+
| Custom Fields | Per-object field sets + length/precision/scale changes |
|
|
124
|
+
| Validation Rules | Active/inactive state differences + per-org-only rules |
|
|
125
|
+
| Flows | MasterLabel, process type, API version, status |
|
|
126
|
+
| Named Credentials | Endpoint URL comparison |
|
|
127
|
+
| Connected Apps | DeveloperName presence comparison |
|
|
128
|
+
| Custom Metadata Types | Presence comparison |
|
|
129
|
+
|
|
130
|
+
Deep data differences are mapped back to the summary statistics table via metadata enrichment, so the Identical/Different columns reflect actual content-level changes, not just name-level overlap.
|
|
131
|
+
|
|
132
|
+
### Org-Level
|
|
133
|
+
|
|
134
|
+
| Category | Source | Depth |
|
|
135
|
+
|----------|--------|-------|
|
|
136
|
+
| Org Settings | Tooling API `OrganizationSettingsDetail` (fallback: `SecurityHealthCheckRisks`) | Enabled/disabled feature comparison + non-boolean value diffs |
|
|
137
|
+
| Org Limits | REST `/limits/` endpoint | Max values for API, storage, features |
|
|
138
|
+
| Profiles | Tooling API | Name-level (shared/Source-only/Target-only) |
|
|
139
|
+
| System Permissions | Tooling API `PermissionSet` | PermissionsXxx field comparison per permission set |
|
|
140
|
+
| Installed Packages | Tooling API `InstalledSubscriberPackage` | Name + namespace + version comparison |
|
|
141
|
+
| Package Components | Metadata listing | Namespaced components grouped by namespace |
|
|
142
|
+
| Licenses | SOQL | User, Permission Set, and Package license counts |
|
|
143
|
+
|
|
144
|
+
## Scripts
|
|
145
|
+
|
|
146
|
+
### `collect_org_data.py`
|
|
147
|
+
|
|
148
|
+
Collects all data from a single org into a **split directory** with per-category JSON files.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
python3 ./scripts/collect_org_data.py \
|
|
152
|
+
--org-alias my-dev-org \
|
|
153
|
+
--output /tmp/org-data
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Flag | Purpose |
|
|
157
|
+
|------|---------|
|
|
158
|
+
| `--org-alias` | SF CLI alias or username for the org (required) |
|
|
159
|
+
| `--output` | Output directory path (required) |
|
|
160
|
+
| `--skip-deep-data` | Skip Tooling API content-level queries (faster, less detail) |
|
|
161
|
+
|
|
162
|
+
**Output directory structure:**
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
/tmp/org-data/
|
|
166
|
+
manifest.json # Lists category files
|
|
167
|
+
identity.json # Org ID, name, edition, errors
|
|
168
|
+
metadata.json # Metadata types and components
|
|
169
|
+
permissions.json # Org settings (OrganizationSettingsDetail)
|
|
170
|
+
org_values.json # Org limits
|
|
171
|
+
packages.json # Installed packages
|
|
172
|
+
licenses.json # User, PSL, and package licenses
|
|
173
|
+
system_permissions.json # PermissionsXxx on PermissionSet
|
|
174
|
+
deep_data.json # Apex bodies, Flow state, etc.
|
|
175
|
+
combined.json # All-in-one (backward compatibility)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Split files enable parallel agent access — each agent loads only the section it needs.
|
|
179
|
+
|
|
180
|
+
**SOQL pagination:** Queries returning >2000 records are automatically paginated via `nextRecordsUrl`.
|
|
181
|
+
|
|
182
|
+
**Parallelized collection:** Metadata listing uses 12 workers, deep data queries use 8 workers via `ThreadPoolExecutor`.
|
|
183
|
+
|
|
184
|
+
Exit codes: `0` success, `1` fatal error, `2` session expired (re-auth and retry).
|
|
185
|
+
|
|
186
|
+
### `compute_diff.py`
|
|
187
|
+
|
|
188
|
+
Computes the comparison between two collected org data sets.
|
|
189
|
+
Accepts either a split directory or a single JSON file for each org.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
python3 ./scripts/compute_diff.py \
|
|
193
|
+
--org-a /tmp/source \
|
|
194
|
+
--org-b /tmp/target \
|
|
195
|
+
--output /tmp/org-comparison \
|
|
196
|
+
--format both \
|
|
197
|
+
--org-a-label "Source" \
|
|
198
|
+
--org-b-label "Target"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| Flag | Purpose |
|
|
202
|
+
|------|---------|
|
|
203
|
+
| `--org-a` | Path to org A data directory or JSON file (required) |
|
|
204
|
+
| `--org-b` | Path to org B data directory or JSON file (required) |
|
|
205
|
+
| `--output` | Output path prefix (required) |
|
|
206
|
+
| `--format` | `markdown`, `json`, or `both` (default: `both`) |
|
|
207
|
+
| `--org-a-label` | Display name for org A in the report (default: "Source") |
|
|
208
|
+
| `--org-b-label` | Display name for org B in the report (default: "Target") |
|
|
209
|
+
| `--show-shared` | Include shared components in the detailed breakdown |
|
|
210
|
+
|
|
211
|
+
Outputs `<prefix>.json` (structured data) and `<prefix>.md` (formatted report).
|
|
212
|
+
|
|
213
|
+
### `introspect_org.py`
|
|
214
|
+
|
|
215
|
+
Generates a single-org introspection report from a previously collected org data directory (output of `collect_org_data.py`). Covers metadata inventory, org settings, org limits, installed packages, licenses, system permissions, and deep data.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
python3 ./scripts/introspect_org.py \
|
|
219
|
+
--org /tmp/org-data \
|
|
220
|
+
--output /tmp/report \
|
|
221
|
+
--format both \
|
|
222
|
+
--label "My Production Org"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
| Flag | Purpose |
|
|
226
|
+
|------|---------|
|
|
227
|
+
| `--org` | Path to collected org data directory or JSON file (required) |
|
|
228
|
+
| `--output` | Output path prefix (required) |
|
|
229
|
+
| `--format` | `markdown`, `json`, or `both` (default: `both`) |
|
|
230
|
+
| `--label` | Display name for the org in the report (default: org alias or directory name) |
|
|
231
|
+
|
|
232
|
+
Outputs `<prefix>.json` (structured data) and/or `<prefix>.md` (formatted report) depending on `--format`.
|
|
233
|
+
|
|
234
|
+
## Security
|
|
235
|
+
|
|
236
|
+
- **Read-only** — Never deploys, modifies, or deletes anything in either org.
|
|
237
|
+
- **SF CLI auth only** — All authentication through SF CLI. No raw credentials, session IDs, or access tokens accepted.
|
|
238
|
+
|
|
239
|
+
## Use Cases
|
|
240
|
+
|
|
241
|
+
### Compare sandbox against production
|
|
242
|
+
|
|
243
|
+
Ensure sandbox is in sync before deploying changes.
|
|
244
|
+
|
|
245
|
+
### Audit org configuration drift
|
|
246
|
+
|
|
247
|
+
Detect unexpected changes between environments.
|
|
248
|
+
|
|
249
|
+
### Compare dev org against customer org
|
|
250
|
+
|
|
251
|
+
Identify what metadata/configuration your package adds or modifies.
|
|
252
|
+
|
|
253
|
+
### Analyze a single org
|
|
254
|
+
|
|
255
|
+
Inventory all metadata, settings, permissions, packages, and licenses in an org before making changes or onboarding a new environment.
|
|
256
|
+
|
|
257
|
+
## Known Limitations
|
|
258
|
+
|
|
259
|
+
- Deep data body queries may timeout on large orgs (use `--skip-deep-data` to work around)
|
|
260
|
+
- Edition differences (DE vs EE) create inherent drift that isn't configuration drift
|
|
261
|
+
- Deep data comparison sections are capped at 50 entries in the report (full data in JSON output)
|
|
262
|
+
- `OrganizationSettingsDetail` availability varies by org edition — falls back to `SecurityHealthCheckRisks` which preserves raw values but may include non-boolean settings
|
|
263
|
+
- Non-namespaced package components (e.g. unlocked 2GP) cannot be distinguished from org-native metadata
|
|
264
|
+
|
|
265
|
+
## Tips
|
|
266
|
+
|
|
267
|
+
- **Edition-aware:** DE vs EE orgs will always show permission/profile drift — this is expected
|
|
268
|
+
- **Session expiry:** If your session expires, re-run `sf org login web` and retry
|
|
269
|
+
- **Large orgs:** Use `--skip-deep-data` for a faster initial pass; run full deep data on targeted follow-ups
|
|
270
|
+
- **Custom labels:** Use `--org-a-label` / `--org-b-label` to make the report human-readable
|
|
271
|
+
- **Split files:** For large orgs, agents can load individual category files in parallel instead of the full combined.json
|
|
272
|
+
|
|
273
|
+
## Skill Structure
|
|
274
|
+
|
|
275
|
+
```text
|
|
276
|
+
dx-org-analyze/
|
|
277
|
+
├── SKILL.md # Workflow, rules, and reference index
|
|
278
|
+
├── scripts/
|
|
279
|
+
│ ├── collect_org_data.py # Per-org data collection (SF CLI + Tooling API)
|
|
280
|
+
│ ├── compute_diff.py # Diff computation, drift scoring, report generation
|
|
281
|
+
│ └── introspect_org.py # Single-org introspection report generation
|
|
282
|
+
├── references/
|
|
283
|
+
│ ├── collection-details.md # What the collection script gathers and how
|
|
284
|
+
│ └── report-format.md # Drift score formula and report sections
|
|
285
|
+
└── tests/
|
|
286
|
+
├── test_consistency.sh # SKILL.md and script structural tests
|
|
287
|
+
├── test_compute_diff.sh # Diff computation tests with fixture data
|
|
288
|
+
├── fixtures/ # Sample org data for testing
|
|
289
|
+
│ ├── org_a.json
|
|
290
|
+
│ └── org_b.json
|
|
291
|
+
└── README.md # Test documentation
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Tests
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Run all consistency tests (no credentials needed)
|
|
298
|
+
bash tests/test_consistency.sh
|
|
299
|
+
# Expected: 74 passed, 0 failed
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Contributing
|
|
303
|
+
|
|
304
|
+
To extend the comparison categories, update:
|
|
305
|
+
|
|
306
|
+
1. `scripts/collect_org_data.py` — Add a query to `collect_deep_data()` or a new collection function
|
|
307
|
+
2. `scripts/compute_diff.py` — Add comparison logic (e.g. in `diff_deep_data()`) and rendering in `render_markdown()`; if adding a new enrichable type, update `enrich_metadata_with_deep_diffs()`
|
|
308
|
+
3. `SKILL.md` — Update step descriptions if the workflow changes
|
|
309
|
+
4. `tests/test_consistency.sh` — Add assertions for new sections
|
|
310
|
+
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dx-org-analyze
|
|
3
|
+
description: "Compare two Salesforce orgs side-by-side and produce a comparison report with drift score, or analyze a single org to produce an inventory covering metadata components, org permissions, system permissions, profiles, installed packages, licenses, and org limits. Use this skill when the user wants to compare orgs, diff orgs, audit configuration drift, find what changed between two environments, compare sandbox against production, or analyze a single org. Trigger phrases include: 'compare orgs', 'compare these 2 orgs', 'compare my orgs', 'diff orgs', 'org diff', 'what is different between these orgs', 'compare metadata', 'org comparison', 'audit org differences', 'compare sandbox to production', 'introspect org', 'org inventory', 'what is in my org', 'analyze org', 'org analysis', 'analyze my org', 'analyze my Salesforce org'. Do NOT use for retrieving metadata (use platform-metadata-retrieve), deploying metadata (use platform-metadata-deploy), or switching orgs (use dx-org-switch)."
|
|
4
|
+
allowed-tools: Bash Read
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.0"
|
|
7
|
+
minApiVersion: "62.0"
|
|
8
|
+
domains: ["Developer Experience"]
|
|
9
|
+
relatedSkills:
|
|
10
|
+
- "dx-org-manage"
|
|
11
|
+
- "dx-org-switch"
|
|
12
|
+
- "platform-metadata-deploy"
|
|
13
|
+
- "platform-metadata-retrieve"
|
|
14
|
+
cliTools:
|
|
15
|
+
- tool: ["python3"]
|
|
16
|
+
semver: ">=3.10"
|
|
17
|
+
- tool: ["sf"]
|
|
18
|
+
semver: ">=2.0.0"
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Tool Restrictions
|
|
22
|
+
|
|
23
|
+
**Use ONLY the Bash tool** to execute all `sf` CLI commands and Python scripts. Always include `--json` for `sf` commands. Do NOT use `mcp__salesforce_dx__*` tools.
|
|
24
|
+
|
|
25
|
+
**Output artifacts for eval/testing:** When an output directory is available, write results there. After executing the skill: (1) if the user specified an output path, write there immediately; (2) otherwise run `[ -d force-app/main/adk-eval-output/ ] && echo 'force-app/main/adk-eval-output'` to detect the eval directory; (3) write the full JSON result to `<output-dir>/dx-org-analyze-result.json` and the markdown report to `<output-dir>/dx-org-analyze-result.md`.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Data Access Hierarchy
|
|
30
|
+
|
|
31
|
+
When querying org data, always follow this priority order:
|
|
32
|
+
|
|
33
|
+
1. **SF CLI commands** — Preferred. Use `sf org list metadata-types`, `sf org list metadata`, `sf org display`, `sf data query`, `sf limits api display`, etc.
|
|
34
|
+
2. **Direct REST/Tooling API calls** — Last resort, only when SF CLI cannot provide the data.
|
|
35
|
+
|
|
36
|
+
Never bypass this hierarchy. If an SF CLI command exists for the operation, use it even if a direct API call would be simpler.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Authentication Rules
|
|
41
|
+
|
|
42
|
+
- **All authentication MUST go through SF CLI** (`sf org login web`, `sf org login jwt`, `sf org login access-token`).
|
|
43
|
+
- Never accept raw credentials (username + password), session IDs, or access tokens directly from the user.
|
|
44
|
+
- The collection script obtains its access token exclusively via `sf org display --json`.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Workflow
|
|
49
|
+
|
|
50
|
+
### Step 0: List Authenticated Orgs
|
|
51
|
+
|
|
52
|
+
Run this command to discover all authenticated orgs:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
sf org list --json --skip-connection-status
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Parse the JSON output. Collect orgs from **all buckets** (`devHubs`, `nonScratchOrgs`, `scratchOrgs`, `sandboxes`, `other`). Present authenticated orgs in a readable table:
|
|
59
|
+
|
|
60
|
+
| # | Alias | Username | Instance URL | Org ID | Type |
|
|
61
|
+
|---|-------|----------|--------------|--------|------|
|
|
62
|
+
|
|
63
|
+
If fewer than 2 orgs are authenticated but at least 1 is available, offer the **single-org introspect** mode (see Introspect Workflow below). If no orgs are authenticated, STOP and advise:
|
|
64
|
+
> You need at least 1 authenticated org. Run `sf org login web --alias <name>` to authenticate.
|
|
65
|
+
|
|
66
|
+
If the user explicitly requests a single-org introspection or inventory, use the **Introspect Workflow** regardless of how many orgs are available.
|
|
67
|
+
|
|
68
|
+
### Step 1: User Selects Two Orgs
|
|
69
|
+
|
|
70
|
+
Ask the user to select two orgs from the list. Both must be explicitly named — do NOT allow implicit/default orgs.
|
|
71
|
+
|
|
72
|
+
> Select two orgs to compare. Which is the **source** (reference/expected state)? Which is the **target** (to compare against)?
|
|
73
|
+
|
|
74
|
+
Accept: alias, username, or number from the list. Resolve each selection to a concrete **username**. If an org lacks an alias, prompt the user to assign one. Confirm:
|
|
75
|
+
|
|
76
|
+
> Comparing:
|
|
77
|
+
> - **Source**: `<alias>` (`<username>`)
|
|
78
|
+
> - **Target**: `<alias>` (`<username>`)
|
|
79
|
+
|
|
80
|
+
### Step 2: Validate Connectivity
|
|
81
|
+
|
|
82
|
+
For each org, confirm reachability with a lightweight query that does not expose secrets:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
sf data query --target-org <alias-or-username> --query "SELECT Id FROM Organization LIMIT 1" --json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If the query succeeds (exit 0 and a record is returned), the org is connected. If it fails with `INVALID_SESSION_ID` or auth errors:
|
|
89
|
+
```bash
|
|
90
|
+
sf org login web --alias <alias>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Step 3: Collect Data (per org)
|
|
94
|
+
|
|
95
|
+
Generate a unique run ID and run the collection script for each org:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
RUN_ID=$(date +%Y%m%d-%H%M%S)
|
|
99
|
+
|
|
100
|
+
python3 ./scripts/collect_org_data.py \
|
|
101
|
+
--org-alias "$SOURCE_ORG" \
|
|
102
|
+
--output /tmp/dx-org-comparison-${RUN_ID}-source
|
|
103
|
+
|
|
104
|
+
python3 ./scripts/collect_org_data.py \
|
|
105
|
+
--org-alias "$TARGET_ORG" \
|
|
106
|
+
--output /tmp/dx-org-comparison-${RUN_ID}-target
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Exit codes:** `0` = success, `1` = fatal error (report stderr to user), `2` = session expired (re-authenticate Step 2 and retry).
|
|
110
|
+
|
|
111
|
+
For details on what the collection script gathers, see `references/collection-details.md`.
|
|
112
|
+
|
|
113
|
+
### Step 4: Compute Diff and Report
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
python3 ./scripts/compute_diff.py \
|
|
117
|
+
--org-a /tmp/dx-org-comparison-${RUN_ID}-source \
|
|
118
|
+
--org-b /tmp/dx-org-comparison-${RUN_ID}-target \
|
|
119
|
+
--output /tmp/dx-org-comparison-${RUN_ID} \
|
|
120
|
+
--format both \
|
|
121
|
+
--org-a-label "Source" \
|
|
122
|
+
--org-b-label "Target"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Use `--show-shared` if the user wants shared components listed in detail.
|
|
126
|
+
|
|
127
|
+
### Step 5: Present Results
|
|
128
|
+
|
|
129
|
+
Read `/tmp/dx-org-comparison-${RUN_ID}.md` and present to the user. If the user asks follow-up questions, use `/tmp/dx-org-comparison-${RUN_ID}.json` for data lookups. Then resolve the output directory and copy both files there:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
OUTPUT_DIR=""
|
|
133
|
+
if [ -n "$USER_OUTPUT_PATH" ]; then
|
|
134
|
+
OUTPUT_DIR="$USER_OUTPUT_PATH"
|
|
135
|
+
elif [ -d "force-app/main/adk-eval-output" ]; then
|
|
136
|
+
OUTPUT_DIR="force-app/main/adk-eval-output"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if [ -n "$OUTPUT_DIR" ]; then
|
|
140
|
+
cp /tmp/dx-org-comparison-${RUN_ID}.json "$OUTPUT_DIR/dx-org-analyze-result.json"
|
|
141
|
+
cp /tmp/dx-org-comparison-${RUN_ID}.md "$OUTPUT_DIR/dx-org-analyze-result.md"
|
|
142
|
+
fi
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Introspect Workflow (Single-Org Mode)
|
|
148
|
+
|
|
149
|
+
Use this workflow when the user wants to inspect a single org's configuration, or when only one org is authenticated.
|
|
150
|
+
|
|
151
|
+
### Introspect Step 1: Validate Connectivity
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
sf data query --target-org <alias-or-username> --query "SELECT Id FROM Organization LIMIT 1" --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Introspect Step 2: Collect Data
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
RUN_ID=$(date +%Y%m%d-%H%M%S)
|
|
161
|
+
|
|
162
|
+
python3 ./scripts/collect_org_data.py \
|
|
163
|
+
--org-alias "$ORG" \
|
|
164
|
+
--output /tmp/dx-org-analysis-${RUN_ID}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Introspect Step 3: Generate Report
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
python3 ./scripts/introspect_org.py \
|
|
171
|
+
--org /tmp/dx-org-analysis-${RUN_ID} \
|
|
172
|
+
--output /tmp/dx-org-analysis-${RUN_ID} \
|
|
173
|
+
--format both \
|
|
174
|
+
--label "OrgName"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Introspect Step 4: Present Results
|
|
178
|
+
|
|
179
|
+
Read `/tmp/dx-org-analysis-${RUN_ID}.md` and present to the user. The report covers: metadata inventory, org settings, org limits, installed packages, licenses, system permissions, and deep data records. Then resolve the output directory and copy both files there:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
OUTPUT_DIR=""
|
|
183
|
+
if [ -n "$USER_OUTPUT_PATH" ]; then
|
|
184
|
+
OUTPUT_DIR="$USER_OUTPUT_PATH"
|
|
185
|
+
elif [ -d "force-app/main/adk-eval-output" ]; then
|
|
186
|
+
OUTPUT_DIR="force-app/main/adk-eval-output"
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
if [ -n "$OUTPUT_DIR" ]; then
|
|
190
|
+
cp /tmp/dx-org-analysis-${RUN_ID}.json "$OUTPUT_DIR/dx-org-analyze-result.json"
|
|
191
|
+
cp /tmp/dx-org-analysis-${RUN_ID}.md "$OUTPUT_DIR/dx-org-analyze-result.md"
|
|
192
|
+
fi
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Report Structure
|
|
198
|
+
|
|
199
|
+
The generated report includes:
|
|
200
|
+
|
|
201
|
+
1. **Drift Score** — Weighted overall score (60% metadata, 30% permissions, 10% profiles) with severity level (LOW/MODERATE/HIGH/CRITICAL)
|
|
202
|
+
2. **Summary Statistics** — Counts per metadata type with Identical/Different columns from deep data
|
|
203
|
+
3. **Metadata Components by Type** — Only-in-Source, only-in-Target, shared per type
|
|
204
|
+
4. **Profiles** — Shared, source-only, target-only
|
|
205
|
+
5. **Installed Packages** — Version comparison, only-in-Source, only-in-Target
|
|
206
|
+
6. **Package Components** — Namespaced components grouped by namespace
|
|
207
|
+
7. **Org Permissions** — Boolean enabled/disabled diffs by category, plus value diffs
|
|
208
|
+
8. **System Permissions** — PermissionsXxx fields on PermissionSet, per-set diffs
|
|
209
|
+
9. **Org Values & Limits** — Grouped by Identity, Storage, API, Feature Limits
|
|
210
|
+
10. **Licenses** — User, Permission Set, Package license quantity diffs
|
|
211
|
+
11. **Deep Data** — Content-level diffs for Apex, Flows, Validation Rules, Custom Fields, etc.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Rules / Constraints
|
|
216
|
+
|
|
217
|
+
| Constraint | Rationale |
|
|
218
|
+
|-----------|-----------|
|
|
219
|
+
| Always use `--json` with sf commands | Structured output for reliable parsing |
|
|
220
|
+
| Resolve orgs to usernames, not aliases | Aliases can be ambiguous; usernames are unique |
|
|
221
|
+
| Skip expired and disconnected orgs | Cannot query metadata from inaccessible orgs |
|
|
222
|
+
| Read-only — never deploy or modify | This skill compares only, never mutates either org |
|
|
223
|
+
| SF CLI auth only | All authentication through SF CLI credential store |
|
|
224
|
+
| Both orgs must be explicit | Never compare unnamed or implicit/default orgs |
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Troubleshooting
|
|
229
|
+
|
|
230
|
+
| Issue | Resolution |
|
|
231
|
+
|-------|------------|
|
|
232
|
+
| "No org found for \<alias\>" | Org not authenticated — run `sf org login web --alias <name>` |
|
|
233
|
+
| Fewer than 2 authenticated orgs | Authenticate additional orgs before comparing |
|
|
234
|
+
| "INVALID_SESSION_ID" or auth errors | Session expired — re-run `sf org login web --alias <name>` |
|
|
235
|
+
| Collection script exits with code 2 | Session expired — re-authenticate and retry |
|
|
236
|
+
| API limit errors during metadata listing | Use `--skip-deep-data` for a faster pass with less detail |
|
|
237
|
+
| Edition differences (DE vs EE) | Many differences are edition-inherent, not configuration drift |
|
|
238
|
+
| Large orgs timeout on deep data | Use `--skip-deep-data` flag; run full deep data on targeted follow-ups |
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Cross-Skill Integration
|
|
243
|
+
|
|
244
|
+
| Need | Delegate to |
|
|
245
|
+
|------|-------------|
|
|
246
|
+
| Retrieve specific metadata from an org | `platform-metadata-retrieve` |
|
|
247
|
+
| Deploy metadata to an org | `platform-metadata-deploy` |
|
|
248
|
+
| Create a scratch org for comparison | `dx-org-manage` |
|
|
249
|
+
| Switch default org after comparison | `dx-org-switch` |
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Reference File Index
|
|
254
|
+
|
|
255
|
+
| File | When to read |
|
|
256
|
+
|------|-------------|
|
|
257
|
+
| `references/collection-details.md` | For details on what the collection script gathers and how |
|
|
258
|
+
| `references/report-format.md` | For drift score formula and report section details |
|
|
259
|
+
| `scripts/collect_org_data.py` | Data collection script (per org) |
|
|
260
|
+
| `scripts/compute_diff.py` | Diff computation and report generation script |
|
|
261
|
+
| `scripts/introspect_org.py` | Single-org introspection report script |
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Collection Details
|
|
2
|
+
|
|
3
|
+
The `scripts/collect_org_data.py` script collects all data from a single org into a split directory with per-category JSON files.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python3 ./scripts/collect_org_data.py \
|
|
9
|
+
--org-alias my-dev-org \
|
|
10
|
+
--output /tmp/org-data
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Flag | Purpose |
|
|
14
|
+
|------|---------|
|
|
15
|
+
| `--org-alias` | SF CLI alias or username for the org (required) |
|
|
16
|
+
| `--output` | Output directory path (required) |
|
|
17
|
+
| `--skip-deep-data` | Skip Tooling API content-level queries (faster, less detail) |
|
|
18
|
+
|
|
19
|
+
## Output Directory Structure
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
/tmp/org-data/
|
|
23
|
+
manifest.json # Lists category files
|
|
24
|
+
identity.json # Org ID, name, edition, errors
|
|
25
|
+
metadata.json # Metadata types and components
|
|
26
|
+
permissions.json # Org settings (OrganizationSettingsDetail)
|
|
27
|
+
org_values.json # Org limits
|
|
28
|
+
packages.json # Installed packages
|
|
29
|
+
licenses.json # User, PSL, and package licenses
|
|
30
|
+
system_permissions.json # PermissionsXxx on PermissionSet
|
|
31
|
+
deep_data.json # Apex bodies, Flow state, etc.
|
|
32
|
+
combined.json # All-in-one (backward compatibility)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Split files enable parallel agent access — each agent loads only the section it needs.
|
|
36
|
+
|
|
37
|
+
## What Gets Collected
|
|
38
|
+
|
|
39
|
+
### Metadata (Name-Level)
|
|
40
|
+
|
|
41
|
+
All types discovered via `sf org list metadata-types` (typically 100+ types). Falls back to Tooling API queries if CLI metadata commands are unavailable. Namespaced (managed-package) components are tracked separately.
|
|
42
|
+
|
|
43
|
+
Metadata listing uses 12 parallel workers via `ThreadPoolExecutor`.
|
|
44
|
+
|
|
45
|
+
### Deep Data (Content-Level via Tooling API)
|
|
46
|
+
|
|
47
|
+
| Category | Fields Collected |
|
|
48
|
+
|----------|-----------------|
|
|
49
|
+
| Apex Classes | Body hash (SHA-256), API version, LOC, status |
|
|
50
|
+
| Apex Triggers | Body hash comparison |
|
|
51
|
+
| Custom Fields | Per-object field sets, data type, length, precision, required |
|
|
52
|
+
| Validation Rules | Active/inactive state, error message |
|
|
53
|
+
| Flows | Process type, API version, active version ID |
|
|
54
|
+
| Named Credentials | Endpoint URL |
|
|
55
|
+
| Object Permissions | Per-profile CRUD (Create/Read/Edit/Delete) per object |
|
|
56
|
+
| Field Permissions | Per-profile Read/Edit per field (FLS) |
|
|
57
|
+
| Connected Apps | Presence |
|
|
58
|
+
| Custom Metadata Types | Presence |
|
|
59
|
+
|
|
60
|
+
Deep data queries use 8 parallel workers. SOQL pagination handles >2000 records automatically.
|
|
61
|
+
|
|
62
|
+
### Org Settings
|
|
63
|
+
|
|
64
|
+
Queries `OrganizationSettingsDetail` via Tooling API (enabled/disabled features). Falls back to `SecurityHealthCheckRisks` if unavailable.
|
|
65
|
+
|
|
66
|
+
### Org Limits
|
|
67
|
+
|
|
68
|
+
Uses `sf limits api display` (preferred) or REST `/limits/` endpoint (fallback). Captures max values for API calls, storage, features.
|
|
69
|
+
|
|
70
|
+
### System Permissions
|
|
71
|
+
|
|
72
|
+
Dynamically discovers `PermissionsXxx` boolean fields via `sf sobject describe --sobject PermissionSet`, then queries all custom and profile-owned PermissionSet records. Filters out auto-generated permission sets (ID-based names).
|
|
73
|
+
|
|
74
|
+
### Installed Packages
|
|
75
|
+
|
|
76
|
+
Queries `InstalledSubscriberPackage` via Tooling API. Captures name, namespace, and version.
|
|
77
|
+
|
|
78
|
+
### Licenses
|
|
79
|
+
|
|
80
|
+
Queries `UserLicense`, `PermissionSetLicense`, and `PackageLicense` with total/used counts.
|
|
81
|
+
|
|
82
|
+
## Exit Codes
|
|
83
|
+
|
|
84
|
+
| Code | Meaning |
|
|
85
|
+
|------|---------|
|
|
86
|
+
| `0` | Success |
|
|
87
|
+
| `1` | Fatal error (bad args, network failure, sf CLI not available) |
|
|
88
|
+
| `2` | Session expired (caller should re-authenticate and retry) |
|