@ryanstark24/sfgraph-skills 1.2.0 → 1.6.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 +3 -2
- package/skills/sf-cross-layer-trace/SKILL.md +12 -5
- package/skills/sf-cross-org-diff/SKILL.md +7 -1
- package/skills/sf-dead-code-audit/SKILL.md +10 -3
- package/skills/sf-debug-log-analysis/SKILL.md +6 -2
- package/skills/sf-debug-root-cause/SKILL.md +17 -2
- package/skills/sf-deployment-manifest/SKILL.md +13 -7
- package/skills/sf-explain-code/SKILL.md +16 -2
- package/skills/sf-find-similar/SKILL.md +10 -4
- package/skills/sf-flow-impact/SKILL.md +6 -0
- package/skills/sf-governor-risk-fix/SKILL.md +35 -17
- package/skills/sf-graph-plus-source/SKILL.md +46 -0
- package/skills/sf-graph-router/SKILL.md +44 -2
- package/skills/sf-impact-from-diff/SKILL.md +11 -5
- package/skills/sf-metadata-refresh/SKILL.md +9 -3
- package/skills/sf-omnistudio-migration-audit/SKILL.md +15 -5
- package/skills/sf-schema-overview/SKILL.md +8 -2
- package/skills/sf-security-audit/SKILL.md +10 -4
- package/skills/sf-snapshot-compare/SKILL.md +6 -0
- package/skills/sf-web-explorer/SKILL.md +6 -1
- package/skills/sf-what-broke/SKILL.md +28 -22
- package/skills/sf-wip-impact/SKILL.md +8 -0
- package/dist/__tests__/cursor-format.test.d.ts +0 -2
- package/dist/__tests__/cursor-format.test.d.ts.map +0 -1
- package/dist/__tests__/cursor-format.test.js +0 -44
- package/dist/__tests__/cursor-format.test.js.map +0 -1
- package/dist/__tests__/installer.test.d.ts +0 -2
- package/dist/__tests__/installer.test.d.ts.map +0 -1
- package/dist/__tests__/installer.test.js +0 -64
- package/dist/__tests__/installer.test.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryanstark24/sfgraph-skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"url": "https://github.com/ryanStark24/sfgraph"
|
|
23
23
|
},
|
|
24
24
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
25
|
+
"node": ">=22"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"description": "sfgraph SKILL.md playbooks for Cursor and Claude",
|
|
31
31
|
"files": [
|
|
32
32
|
"dist",
|
|
33
|
+
"!dist/**/__tests__",
|
|
33
34
|
"skills",
|
|
34
35
|
"README.md"
|
|
35
36
|
],
|
|
@@ -21,6 +21,10 @@ tools_used:
|
|
|
21
21
|
|
|
22
22
|
Use when the user needs to follow a single artifact (field, LWC, Apex method) across every layer it touches — UI rendering, Apex controller, flow automation, data model.
|
|
23
23
|
|
|
24
|
+
## Evidence rule — say it only if something backs it
|
|
25
|
+
|
|
26
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
27
|
+
|
|
24
28
|
## When to volunteer this skill
|
|
25
29
|
|
|
26
30
|
Source-only tools (file grep, repo search, `explain_code`) can answer "where is this used?" inside the sfdx-source repo, but they cannot see:
|
|
@@ -37,16 +41,17 @@ Only skip the offer when the user has explicitly scoped the question to source (
|
|
|
37
41
|
|
|
38
42
|
## Playbook
|
|
39
43
|
|
|
40
|
-
1. Resolve the starting node.
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
1. Resolve the starting node. `cross_layer_flow_map` walks **downstream only**, from an `entry` (LWC, Aura, page, Flow, Apex, OmniScript, FlexCard, Integration Procedure) toward the data it touches. A field or object entry reaches nothing.
|
|
45
|
+
- **"Where is this field / component used"** is an upstream question: call `analyze_field(object, field)` for a field's direct readers and writers, or `trace_upstream` on the component's qname for the pages and components that reach it. Then run step 2 from each UI or automation entry you found.
|
|
46
|
+
2. Call `cross_layer_flow_map(entry=…)` with the resolved entry. Receive ordered paths across layers (UI -> Controller -> Service -> Selector -> SObject and any Flow / Process Builder branches).
|
|
47
|
+
3. For each distinct path, summarise the layer transitions with the edge type that bridges them (`@wire`, `@AuraEnabled`, `READS_FIELD`, `WRITES_FIELD`, `FLOW_INVOKES_APEX`).
|
|
43
48
|
4. Surface anything anomalous: cycles, missing `with sharing`, fields read by multiple LWCs but written only by Flow, etc.
|
|
44
49
|
5. Render the Mermaid layered diagram from `cross_layer_flow_map`. If the graph has more than 30 nodes, ask whether to filter to a single layer pair.
|
|
45
50
|
6. End with a one-line recommendation of which related skill to invoke next (e.g. `sf-security-audit` if FLS gaps appear).
|
|
46
51
|
|
|
47
52
|
## Visualization
|
|
48
53
|
|
|
49
|
-
Render a **`flowchart TD`** layered diagram with one subgraph per layer (UI / Controller / Service / Selector / Data). Edges are labelled with the edge type that bridges layers (`@wire`, `@AuraEnabled`, `READS_FIELD`, `
|
|
54
|
+
Render a **`flowchart TD`** layered diagram with one subgraph per layer (UI / Controller / Service / Selector / Data). Edges are labelled with the edge type that bridges layers (`@wire`, `@AuraEnabled`, `READS_FIELD`, `WRITES_FIELD`, `FLOW_INVOKES_APEX`).
|
|
50
55
|
|
|
51
56
|
```
|
|
52
57
|
flowchart TD
|
|
@@ -71,6 +76,8 @@ If you drop down to `trace_upstream` / `trace_downstream` for a raw dependency w
|
|
|
71
76
|
|
|
72
77
|
## Staleness check
|
|
73
78
|
|
|
79
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
80
|
+
|
|
74
81
|
Before calling `cross_layer_flow_map`, invoke `staleness_check` for the target org. If the report says stale, surface a warning to the user:
|
|
75
82
|
|
|
76
83
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -88,7 +95,7 @@ Then continue with the playbook.
|
|
|
88
95
|
## Don't
|
|
89
96
|
|
|
90
97
|
- Do not collapse layers — the value of this skill is showing the layer boundaries explicitly.
|
|
91
|
-
-
|
|
98
|
+
- `cross_layer_flow_map` has no depth parameter; if its map is too broad, narrow by starting from a nearer entry, or use `trace_downstream` with `depth`.
|
|
92
99
|
- Do not modify anything. Pure read.
|
|
93
100
|
- Do not assume the starting node exists; if `analyze_field` returns empty, ask the user to refine.
|
|
94
101
|
- Do not stop at a source-only answer for LWC / Aura / ApexPage / field / Apex method questions without at least *offering* the cross-layer trace. Repo grep misses FlexiPage bindings, Quick Action targets, and the Apex→SObject tail — say so and offer to fill it in.
|
|
@@ -18,10 +18,14 @@ tools_used:
|
|
|
18
18
|
|
|
19
19
|
Use when the user wants to understand drift between two orgs — typically pre-deploy verification or post-deploy reconciliation.
|
|
20
20
|
|
|
21
|
+
## Evidence rule — say it only if something backs it
|
|
22
|
+
|
|
23
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
24
|
+
|
|
21
25
|
## Playbook
|
|
22
26
|
|
|
23
27
|
1. Call `list_orgs` first. Present the user with the authenticated orgs, each org's `ingested` flag, last sync timestamp, and `stale` flag. Ask the user to pick the two orgs to compare. Refuse to proceed if either selected org has `ingested=false` — instruct them to run `sfgraph ingest --org <alias>` first.
|
|
24
|
-
2. Call `cross_org_diff(
|
|
28
|
+
2. Call `cross_org_diff(org_a, org_b)` (optionally `category`). Capture added / removed / modified nodes grouped by metadata category. Managed-package and standard items found in one org only are hidden by default and counted in the report; pass `include_managed: true` / `include_standard: true` only when the user asks about them.
|
|
25
29
|
3. For high-churn categories, call `point_in_time_diff` inside each org to confirm the drift originated there (vs being a stale snapshot artifact).
|
|
26
30
|
4. Categorise drift: **structural** (new objects/fields), **logic** (Apex, Flow body changes), **security** (profile / sharing), **config** (custom metadata / settings).
|
|
27
31
|
5. Render the Mermaid drift summary from the tool. For each category, list the top-N items by impact.
|
|
@@ -48,6 +52,8 @@ For diffs with >50 nodes, switch to a bucketed table per category and skip the d
|
|
|
48
52
|
|
|
49
53
|
## Staleness check
|
|
50
54
|
|
|
55
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
56
|
+
|
|
51
57
|
Before calling `cross_org_diff`, invoke `staleness_check` for both orgs. If either is stale, surface a warning to the user:
|
|
52
58
|
|
|
53
59
|
> Your ingest for `<alias>` is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -19,16 +19,21 @@ tools_used:
|
|
|
19
19
|
|
|
20
20
|
Use when the user wants to inventory metadata that appears unreferenced. Output must be bucketed by confidence — deletion in Salesforce is reversible only via backup, so we communicate uncertainty.
|
|
21
21
|
|
|
22
|
+
## Evidence rule — say it only if something backs it
|
|
23
|
+
|
|
24
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
25
|
+
|
|
22
26
|
## Playbook
|
|
23
27
|
|
|
24
|
-
1. Call `dead_code_audit` for the org. The tool **summarizes by confidence (`high`/`medium`/`low`) and by metadata type**, then renders a capped list (`limit`, default 50). On a large org there can be thousands of candidates — do NOT try to retrieve them all inline; work from the summary counts and drill in with `confidence: "high"` (most-likely-dead first) or raise `limit`. For the complete machine-readable set, use `export_sarif`. Each row carries a score + reasons (`no_incoming_edges`, `stale_freshness`).
|
|
25
|
-
|
|
28
|
+
1. Call `dead_code_audit` for the org. The tool **summarizes by confidence (`high`/`medium`/`low`) and by metadata type**, then renders a capped list (`limit`, default 50). On a large org there can be thousands of candidates — do NOT try to retrieve them all inline; work from the summary counts and drill in with `confidence: "high"` (most-likely-dead first) or raise `limit`. For the complete machine-readable set, use `export_sarif`. Each row carries a score + reasons (`no_incoming_edges`, `test_class` — an `@isTest` class nothing references, flagged `isTest: true`, `stale_freshness`). Test references count as references: production code that only tests reference is not dead and is listed apart in `referencedOnlyByTests` (reason `referenced_only_by_tests`); deleting it means deleting those tests too.
|
|
29
|
+
- **Unused fields:** call `dead_code_audit(label="CustomField")` (optionally `pattern="CustomField:Account.*"`). It lists fields that nothing reads or writes. A field on a page layout or in a report still counts as used; profile and permission-set access does not. Fields have no freshness score, so there is no confidence grade: treat each one as **likely-dead** until checked.
|
|
30
|
+
2. Confidence already reflects last-modified dates (`stale_freshness`). `freshness_report` is an org-wide view of what changed recently (20 rows per bucket); it cannot be filtered to the candidates, so use it only for context, not per candidate.
|
|
26
31
|
3. For any candidate the user names interactively, call `trace_upstream` to confirm absence of indirect callers (dynamic Apex, Flow lookups, callable interfaces). Optionally call `find_similar(qname=<candidate>, k=5)` — if the candidate has near-neighbours (similarity > 0.6) that ARE referenced, the candidate may be a copy-paste duplicate of live code and worth deleting; if every neighbour is also in the dead list, you've found a whole disused subsystem worth flagging as a group.
|
|
27
32
|
4. Sort candidates into three buckets:
|
|
28
33
|
- **confident-dead** — no incoming edges, no dynamic-invocation signature match, freshness > 12 months, no inclusion in active permission sets / page layouts.
|
|
29
34
|
- **likely-dead** — no incoming edges, but at least one weak signal (recently modified, referenced from a managed package boundary, or named in a metadata file we don't parse fully).
|
|
30
35
|
- **suspicious-uncertain** — looks dead but the graph has known blind spots (dynamic SOQL string-interpolated field name, reflection-style `Type.forName`, external system reference).
|
|
31
|
-
5. Render
|
|
36
|
+
5. Render a bar-chart / treemap as Mermaid yourself, grouped by category, by counting the candidates the report lists (each line names its kind and confidence). The tool does not draw a diagram.
|
|
32
37
|
6. For confident-dead items, propose a destructive-changes.xml snippet but do not save it. The user owns the delete decision.
|
|
33
38
|
|
|
34
39
|
## Visualization
|
|
@@ -50,6 +55,8 @@ When the org has >100 dead candidates, drop the diagram and rely on the per-buck
|
|
|
50
55
|
|
|
51
56
|
## Staleness check
|
|
52
57
|
|
|
58
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
59
|
+
|
|
53
60
|
Before calling `dead_code_audit`, invoke `staleness_check` for the target org. Stale ingests make dead-code analysis dangerously wrong (a recently-added caller would be invisible). If the report says stale, surface a warning to the user:
|
|
54
61
|
|
|
55
62
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -34,6 +34,10 @@ a `for` over `Trigger.new` — here are its 3 callers", not "looks like a CPU is
|
|
|
34
34
|
|
|
35
35
|
Format reference: <https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_debugging_debug_log.htm>
|
|
36
36
|
|
|
37
|
+
## Evidence rule — say it only if something backs it
|
|
38
|
+
|
|
39
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
40
|
+
|
|
37
41
|
## Getting the log (if the user hasn't supplied one)
|
|
38
42
|
|
|
39
43
|
- Salesforce CLI: `sf apex list log` → `sf apex get log --log-id <id> > debug.log`, or live: `sf apex tail log --color none`.
|
|
@@ -72,10 +76,10 @@ The header line lists the API version + per-category log levels. Every subsequen
|
|
|
72
76
|
- Governor → which limit, the count vs cap, and the `CODE_UNIT`/method where consumption spiked (repeated SOQL/DML lines under one unit).
|
|
73
77
|
- Performance → the `CODE_UNIT`/method with the largest elapsed delta.
|
|
74
78
|
4. **Ground it in the graph** (the part a log reader alone can't do):
|
|
75
|
-
- `find_nodes ApexClass:<Name>` / `ApexMethod:<Name>.*` to resolve the anchor to a qname; `explain_code`
|
|
79
|
+
- `find_nodes ApexClass:<Name>` / `ApexMethod:<Name>.*` to resolve the anchor to a qname; `explain_code` reads stored source for Apex methods, trigger bodies (`ApexTrigger:<Name>`), and Flows (`Flow:<Name>`). For a **whole class, LWC JS, or managed code the graph has no source** — fetch it (local `force-app/main/default/{classes,triggers,flows}/...` or `sf project retrieve start -m "ApexTrigger:<Name>"`); don't analyse from an empty stub. See `sf-explain-code`.
|
|
76
80
|
- `trace_upstream` — who invokes this unit, and in what context (trigger vs batch vs LWC). Order-of-execution and "why was this even called" answers live here.
|
|
77
81
|
- `trace_downstream` — what it calls/reads/writes; a NullPointer or limit usually originates in a callee.
|
|
78
|
-
- Governor breach → `governor_risk_check`
|
|
82
|
+
- Governor breach → `governor_risk_check` (it scans the whole org; keep the findings for that class) to find the in-loop query or DML the log proved is hot. Its evidence is the query text or DML target, not a line number.
|
|
79
83
|
- Field misbehaviour seen in the log → `analyze_field` for every trigger/Flow/validation that fires on that field (competing automation).
|
|
80
84
|
5. **State a single root-cause hypothesis** backed by *both* the log evidence and the graph evidence, then hand the fix to the right design skill (`sf-architect-apex` / `sf-architect-performance` / `sf-governor-risk-fix`) — grounded, never generic.
|
|
81
85
|
|
|
@@ -31,6 +31,10 @@ same object, a recent change upstream, an order-of-execution surprise, or automa
|
|
|
31
31
|
developer forgot exists. The graph makes those visible. Debug by narrowing with evidence,
|
|
32
32
|
never by pattern-matching the error message to a generic fix.
|
|
33
33
|
|
|
34
|
+
## Evidence rule — say it only if something backs it
|
|
35
|
+
|
|
36
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
37
|
+
|
|
34
38
|
## Method (scientific, evidence-first)
|
|
35
39
|
|
|
36
40
|
0. **If a debug log is available, start there.** A debug log is the strongest anchor — it
|
|
@@ -45,8 +49,12 @@ never by pattern-matching the error message to a generic fix.
|
|
|
45
49
|
- "Field X isn't updating" → `analyze_field` on `<Object>.<Field>`.
|
|
46
50
|
If you can't anchor it, ask the user for the class/object name — don't guess.
|
|
47
51
|
|
|
48
|
-
2. **Read the failing unit.** `explain_code(qname
|
|
49
|
-
|
|
52
|
+
2. **Read the failing unit.** `explain_code(qname=…)` reads stored source for Apex methods
|
|
53
|
+
(`ApexMethod:…`), trigger bodies (`ApexTrigger:…`), and Flows (`Flow:…`). For a **whole
|
|
54
|
+
class, LWC JS, or managed/`(hidden)` code the graph has no source** — fetch it: read the local
|
|
55
|
+
sfdx file (`force-app/main/default/{classes,triggers,flows}/<Name>.{cls,trigger,flow-meta.xml}`)
|
|
56
|
+
or `sf project retrieve start -m "ApexTrigger:<Name>"`. See `sf-explain-code` for the full
|
|
57
|
+
fallback. Confirm the trace's line maps to a real branch (SOQL, DML, deref).
|
|
50
58
|
|
|
51
59
|
3. **Look outward for the real cause — this is where graphs beat grep:**
|
|
52
60
|
- `trace_downstream(qname)` — what it calls / reads / writes. A `System.NullPointer` or
|
|
@@ -56,6 +64,11 @@ never by pattern-matching the error message to a generic fix.
|
|
|
56
64
|
- For an object symptom: `sf-flow-impact` / `analyze_field` — every trigger, Flow,
|
|
57
65
|
Process Builder, and validation rule that fires on the object. "Trigger isn't firing"
|
|
58
66
|
and "field reverts" are almost always a competing automation.
|
|
67
|
+
- **If the cause depends on actual data** (a field's real value, a CMDT/setting the code
|
|
68
|
+
reads, a record-type/picklist value, data skew) and the graph doesn't hold it, pull it
|
|
69
|
+
from the org — config/metadata records freely (`sf data query … FROM <Type>__mdt`), live
|
|
70
|
+
business records only as a small bounded `LIMIT` sample (real/PII data — see
|
|
71
|
+
`sf-graph-router` → "When the graph doesn't hold what the analysis needs").
|
|
59
72
|
- Governor `LimitException` → hand to `sf-governor-risk-fix` scoped to the class to find
|
|
60
73
|
the SOQL/DML-in-loop.
|
|
61
74
|
|
|
@@ -72,6 +85,8 @@ never by pattern-matching the error message to a generic fix.
|
|
|
72
85
|
|
|
73
86
|
## Staleness check
|
|
74
87
|
|
|
88
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
89
|
+
|
|
75
90
|
Call `staleness_check` first. A stale graph misleads root-cause analysis (the change that
|
|
76
91
|
caused the bug may not be ingested yet). If stale, warn and recommend
|
|
77
92
|
`sfgraph ingest --org <alias>` before trusting the upstream/recent-change steps.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sf-deployment-manifest
|
|
3
|
-
description: Generate
|
|
3
|
+
description: Generate package.xml and destructiveChanges.xml from the difference between two ingested orgs. The tool adds no dependencies; extra ones are added by hand with the user.
|
|
4
4
|
triggers:
|
|
5
5
|
- "deployment manifest"
|
|
6
6
|
- "package.xml"
|
|
@@ -17,18 +17,22 @@ tools_used:
|
|
|
17
17
|
|
|
18
18
|
Use when the user wants a `package.xml` that captures a set of changes plus everything they transitively depend on for a successful deploy.
|
|
19
19
|
|
|
20
|
+
## Evidence rule — say it only if something backs it
|
|
21
|
+
|
|
22
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
23
|
+
|
|
20
24
|
## Playbook
|
|
21
25
|
|
|
22
26
|
1. Establish the source and target orgs. The tool's contract today is a strict cross-org diff: it inputs `from_org` (source) and `to_org` (target) and emits the metadata that exists in `from_org` but is missing or different in `to_org` (plus a `destructiveChanges.xml` for things present only in `to_org`).
|
|
23
27
|
2. Optionally call `cross_org_diff` first to preview the set difference before generating the manifest XML.
|
|
24
|
-
3. Call `deployment_manifest_gen` with `from_org` + `to_org` (+ optional `category` filter). The tool does
|
|
25
|
-
4.
|
|
28
|
+
3. Call `deployment_manifest_gen` with `from_org` + `to_org` (+ optional `category` filter). The tool does not walk dependency edges: it returns the set difference as `package.xml` / `destructiveChanges.xml`, and the user is responsible for any parent metadata the diff does not surface. Managed-package and standard items found in one org only are left out of both files by default and named in the report; pass `include_managed: true` / `include_standard: true` only when the user asks for them.
|
|
29
|
+
4. Surface the report's notes as warnings: items left out because they have no deployment type, items hidden as managed or standard, and an incomplete comparison. Go through every `destructiveChanges.xml` member with the user before they deploy it.
|
|
26
30
|
5. Emit the `package.xml` inside a fenced code block. Also emit `destructiveChanges.xml` if the diff includes removals.
|
|
27
31
|
6. If extra dependencies are needed beyond the raw diff, ask the user to extend the set manually — do not silently fabricate dependency edges the graph doesn't have.
|
|
28
32
|
|
|
29
|
-
## Visualization
|
|
33
|
+
## Visualization (optional — you build it)
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
`deployment_manifest_gen` returns only the package.xml / destructiveChanges.xml (plus an `incomplete` block flagging dropped/truncated members) — it does NOT return a diagram or walk dependency edges. If a visual helps, render a **`flowchart LR`** closure graph YOURSELF from the manifest's member list: the user's explicit change set is one subgraph; any dependencies you add manually are another. Edge labels carry the dependency reason (`parent-of`, `layout-for`, `required-permission`).
|
|
32
36
|
|
|
33
37
|
```
|
|
34
38
|
flowchart LR
|
|
@@ -47,6 +51,8 @@ If the closure expands past 60 nodes, summarise per metadata type and only rende
|
|
|
47
51
|
|
|
48
52
|
## Staleness check
|
|
49
53
|
|
|
54
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
55
|
+
|
|
50
56
|
Before calling `deployment_manifest_gen`, invoke `staleness_check` for the source org. A stale graph means missing dependencies in the manifest, which fail at deploy time. If the report says stale, surface a warning to the user:
|
|
51
57
|
|
|
52
58
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -56,11 +62,11 @@ Then continue with the playbook.
|
|
|
56
62
|
## Response Shape
|
|
57
63
|
|
|
58
64
|
- **Source set** — what was requested.
|
|
59
|
-
- **
|
|
65
|
+
- **Manual additions** — dependencies the user chose to add (the tool adds none), grouped by metadata type.
|
|
60
66
|
- **Warnings** — managed-package / namespace / ambiguous-dependency notes.
|
|
61
67
|
- **package.xml** — fenced code block.
|
|
62
68
|
- **destructiveChanges.xml** — fenced code block (only if applicable).
|
|
63
|
-
- **Mermaid dependency graph** —
|
|
69
|
+
- **Mermaid dependency graph** — optional, drawn by you (see Visualization).
|
|
64
70
|
|
|
65
71
|
## Don't
|
|
66
72
|
|
|
@@ -20,11 +20,23 @@ tools_used:
|
|
|
20
20
|
|
|
21
21
|
Use this skill when the user wants a plain-English explanation of a specific code unit (an Apex method, an LWC handler, a trigger body). It reads the stored source via the graph's snippet store, generates an annotated explanation, and caches that explanation back so the next caller gets it for free.
|
|
22
22
|
|
|
23
|
+
## Evidence rule — say it only if something backs it
|
|
24
|
+
|
|
25
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
26
|
+
|
|
23
27
|
## Playbook
|
|
24
28
|
|
|
25
|
-
1. **Resolve the target qname
|
|
29
|
+
1. **Resolve the target qname.** Snippets are stored for Apex methods (`ApexMethod:AccountSvc.calculate(N)`, N = arity), Apex triggers (`ApexTrigger:AccountTrigger`) and Flows (`Flow:Lead_Router`, the Flow XML). A class qname like `ApexClass:AccountSvc` has no snippet of its own (it returns "No snippet stored"): enumerate its methods with `find_nodes(pattern="ApexMethod:AccountSvc.*")` and either pick the method the user meant or explain each in turn. For fields and objects there is no snippet: redirect to `analyze_field` / `sf-cross-layer-trace` instead. If the arity is unknown or the name is ambiguous, use `find_nodes` to enumerate candidates and confirm with the user.
|
|
26
30
|
2. **`staleness_check`** for the linked org. If stale, surface a warning so the user knows the cached source may not reflect production.
|
|
27
31
|
3. **`explain_code(qname=X)`** — pull the stored snippet and any prior cached explanation. The tool returns the source text in a fenced code block.
|
|
32
|
+
- **If the graph doesn't have the full source, FETCH it — don't explain from a stub.** The ingest stores source for Apex methods, Apex triggers and Flows, and **nothing for managed/`(hidden)` code**. When `explain_code` returns "No snippet stored" or an empty body, or you need a whole class, get the real source in this order:
|
|
33
|
+
1. **Local sfdx working tree** (fastest, reflects un-deployed changes) — read the file directly:
|
|
34
|
+
- Apex class → `force-app/main/default/classes/<Name>.cls`
|
|
35
|
+
- Apex trigger → `force-app/main/default/triggers/<Name>.trigger`
|
|
36
|
+
- Flow → `force-app/main/default/flows/<Name>.flow-meta.xml`
|
|
37
|
+
(the package dir may differ — check `packageDirectories` in `sfdx-project.json`).
|
|
38
|
+
2. **Retrieve from the org** when there's no local copy: `sf project retrieve start -m "ApexTrigger:<Name>"` (or `ApexClass:<Name>` / `Flow:<Name>`), then read the retrieved file. For an Apex body without a project, `sf data query --use-tooling-api -q "SELECT Body FROM ApexClass WHERE Name='<Name>'"` (or `ApexTrigger`).
|
|
39
|
+
- State which source you used (graph snippet / local file / org retrieve) so the reader knows whether it reflects production or local edits.
|
|
28
40
|
4. **Generate the explanation**, structured as:
|
|
29
41
|
- **One-paragraph summary**: what the method does at a high level.
|
|
30
42
|
- **Annotated source block**: re-emit the source with terse `// → ...` comments after each conditional branch and each loop. Keep comments to one line.
|
|
@@ -47,6 +59,8 @@ flowchart TD
|
|
|
47
59
|
|
|
48
60
|
## Staleness check
|
|
49
61
|
|
|
62
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
63
|
+
|
|
50
64
|
Before reading the snippet, call `staleness_check` for the org. If the ingest is stale:
|
|
51
65
|
|
|
52
66
|
> Your ingest is N days old. The source you're explaining may not match production. Run `sfgraph ingest --org <alias>` to refresh.
|
|
@@ -79,4 +93,4 @@ If the user says yes, hand off to `sf-find-similar` with mode=qname and the same
|
|
|
79
93
|
- Don't write anything to Salesforce.
|
|
80
94
|
- Always render the source inside a fenced code block tagged with the `sourceFormat` returned by `explain_code` (`apex`, `js`, `html`, etc.).
|
|
81
95
|
- Don't fabricate behaviour — if the source is empty or stubbed, say so plainly.
|
|
82
|
-
- Don't pass a class
|
|
96
|
+
- Don't pass a class or field qname expecting source: only `ApexMethod:...(N)`, `ApexTrigger:...` and `Flow:...` have a snippet. On "No snippet stored" for a class, resolve to a method qname rather than reporting failure.
|
|
@@ -24,6 +24,10 @@ tools_used:
|
|
|
24
24
|
|
|
25
25
|
Use this skill when the user wants discovery rather than exact lookup: *"what other Apex methods are like this one"*, *"find code that handles order cancellation"*, *"is there anything similar to `accountTile`?"*. Powered by the in-process MiniLM-L6 embeddings sfgraph produces during every ingest.
|
|
26
26
|
|
|
27
|
+
## Evidence rule — say it only if something backs it
|
|
28
|
+
|
|
29
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
30
|
+
|
|
27
31
|
## When to choose this vs. the other tools
|
|
28
32
|
|
|
29
33
|
| Question shape | Use |
|
|
@@ -80,10 +84,12 @@ xychart-beta
|
|
|
80
84
|
|
|
81
85
|
## Common failure modes & how to handle them
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
+
The report says which case applies in plain words; the reason code below is only in the tool's structured data.
|
|
88
|
+
|
|
89
|
+
- **Search by meaning not set up** (`vector_index_unavailable`) — the org's graph was ingested before embeddings were wired up, or the optional `@ryanstark24/sfgraph-models` install was skipped. Tell the user verbatim what the tool's markdown suggests: re-install with `npm install -g @ryanstark24/sfgraph` and re-ingest with `--rebuild`.
|
|
90
|
+
- **No stored meaning for the qname** (`no_focal_vector`, qname mode) — the qname is wrong or its label isn't embedded. Confirm the qname spelling. If correct, suggest re-trying with `text` mode using the user's description of the node.
|
|
91
|
+
- **Model files missing** (`embedder_unavailable`, text mode) — the MiniLM runtime isn't present. Fall back to qname mode if the user can name a similar node.
|
|
92
|
+
- **Nothing similar** (`no_neighbours`) — genuinely no similar nodes. Suggest dropping the `label` filter, or running structural `trace_upstream` / `trace_downstream` instead.
|
|
87
93
|
|
|
88
94
|
## Response shape
|
|
89
95
|
|
|
@@ -17,6 +17,10 @@ tools_used:
|
|
|
17
17
|
|
|
18
18
|
Use when the user asks which automation references a specific field or object — typically before a field rename, type change, or deletion.
|
|
19
19
|
|
|
20
|
+
## Evidence rule — say it only if something backs it
|
|
21
|
+
|
|
22
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
23
|
+
|
|
20
24
|
## Playbook
|
|
21
25
|
|
|
22
26
|
1. Resolve the target. Call `analyze_field` for a single field; for an SObject, list its fields first and ask the user to narrow if the scope is large.
|
|
@@ -44,6 +48,8 @@ If more than 20 flows reference the field, group leaves by entry-trigger type an
|
|
|
44
48
|
|
|
45
49
|
## Staleness check
|
|
46
50
|
|
|
51
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
52
|
+
|
|
47
53
|
Before calling `analyze_field`, invoke `staleness_check` for the target org. Newly-created or recently-modified Flows are invisible in a stale graph, which makes this skill produce false negatives. If the report says stale, surface a warning to the user:
|
|
48
54
|
|
|
49
55
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sf-governor-risk-fix
|
|
3
|
-
description: Detect governor-limit risks (SOQL
|
|
3
|
+
description: Detect governor-limit risks (SOQL, SOSL and DML in loops, triggers that are not bulk-safe, unbounded queries) and produce a remediation checklist.
|
|
4
4
|
triggers:
|
|
5
5
|
- "governor limit"
|
|
6
6
|
- "SOQL in loop"
|
|
@@ -17,20 +17,36 @@ tools_used:
|
|
|
17
17
|
|
|
18
18
|
Use when the user wants to find Apex code that risks hitting governor limits at scale. The skill produces a prioritised remediation checklist; it never rewrites Apex automatically.
|
|
19
19
|
|
|
20
|
+
## Evidence rule — say it only if something backs it
|
|
21
|
+
|
|
22
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
23
|
+
|
|
20
24
|
## What the tool actually emits
|
|
21
25
|
|
|
22
26
|
`governor_risk_check` reports only the risk types the analyzer can prove from
|
|
23
|
-
the graph.
|
|
27
|
+
the graph. Each record has `risk` (one of the kinds below), the method or trigger
|
|
28
|
+
qname, and evidence (the query text or DML target):
|
|
24
29
|
|
|
25
|
-
- **`soql_in_loop`**
|
|
30
|
+
- **`soql_in_loop`** (high): a SOQL query whose `EXECUTES_SOQL` edge is tagged
|
|
26
31
|
`inLoop` (AST-verified: the query sits inside a for/while/do-while body).
|
|
27
|
-
- **`
|
|
28
|
-
- **`
|
|
32
|
+
- **`sosl_in_loop`** (high): the same for SOSL (`EXECUTES_SOSL`).
|
|
33
|
+
- **`dynamic_soql_in_loop`** (high): `Database.query(...)` inside a loop.
|
|
34
|
+
- **`dml_in_loop`** (high): the same for DML statements.
|
|
35
|
+
- **`no_bulk`** (high): a trigger that doesn't iterate `Trigger.new` (body-scan path).
|
|
36
|
+
- **`unbounded_query`** (medium): a query with no `WHERE` and no `LIMIT`. Only the
|
|
37
|
+
body-scan path (project ingests, where the full query text is available) emits it;
|
|
38
|
+
live-org ingests do not, because the captured query head makes it pure noise.
|
|
39
|
+
|
|
40
|
+
Medium and above are listed by default; `min_severity: "high"` keeps only the loop and
|
|
41
|
+
bulk findings. `includeTests: true` adds test code. There is no class or namespace
|
|
42
|
+
parameter: the tool scans the whole org, so filter its findings to the scope the user
|
|
43
|
+
asked about, and say that you did.
|
|
29
44
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
The check is per method: SOQL in a method that is *called* from a loop is not flagged.
|
|
46
|
+
Say so when the user asks about a call chain.
|
|
47
|
+
|
|
48
|
+
Do **not** promise non-selective queries, callouts in loops, heap size or trigger
|
|
49
|
+
recursion: the tool does not check them.
|
|
34
50
|
|
|
35
51
|
**Live-ingest prerequisite:** `inLoop` tagging comes from the Apex AST parser. A
|
|
36
52
|
graph ingested before that parser change has zero `inLoop` edges, so the check
|
|
@@ -40,22 +56,22 @@ not "the org is clean" — say so explicitly rather than reporting a false all-c
|
|
|
40
56
|
|
|
41
57
|
## Playbook
|
|
42
58
|
|
|
43
|
-
1. Call `governor_risk_check`
|
|
44
|
-
2. Group findings by `
|
|
59
|
+
1. Call `governor_risk_check` for the org, then keep the findings in the requested scope (a class, a namespace, or the whole org). Records are keyed on the **method** qname (`ApexMethod:Class.method(n)`) or the trigger, with `risk` (one of the six kinds above) and evidence (the query text or DML target).
|
|
60
|
+
2. Group findings by `risk`; within each group, sort by call-site fan-in (more callers = higher blast radius). Use `trace_upstream` on the owning class to get fan-in.
|
|
45
61
|
3. For each finding, describe the canonical fix pattern (e.g. "extract the SOQL above the loop and key the result by `Id`") in one or two sentences — do not write the code. To actually design the bulkified fix, hand off to **`sf-architect-apex`** (or **`sf-architect-performance`** for LDV/query-plan work) with these call sites as grounding, so the rewrite respects the org's real selectors and trigger framework rather than a generic template.
|
|
46
|
-
4. Render
|
|
62
|
+
4. Render a Mermaid heat-map (class-by-rule) yourself from the tool's table findings — `governor_risk_check` returns a table + structured data, not a diagram. See **Visualization**.
|
|
47
63
|
5. Produce a checklist the user can copy into a ticket: `- [ ] ClassName.methodName:line — rule — recommended pattern`.
|
|
48
64
|
6. Recommend `sf-impact-from-diff` after the user applies fixes, to verify nothing downstream regressed.
|
|
49
65
|
|
|
50
66
|
## Visualization
|
|
51
67
|
|
|
52
|
-
Render a **`flowchart TD`** heat-map: rows are Apex classes (top N by fan-in), columns are rule ids. Cell colour encodes severity (high/medium/low).
|
|
68
|
+
Render a **`flowchart TD`** heat-map: rows are Apex classes (top N by fan-in), columns are rule ids. Cell colour encodes severity (high/medium/low). Build it yourself from the tool's findings — `governor_risk_check` returns a table, not a diagram.
|
|
53
69
|
|
|
54
70
|
```
|
|
55
71
|
flowchart TD
|
|
56
|
-
ClassA -->|
|
|
57
|
-
ClassA -->|
|
|
58
|
-
ClassB -->|
|
|
72
|
+
ClassA -->|soql_in_loop: high| Risk1[:::hi]
|
|
73
|
+
ClassA -->|unbounded_query: med| Risk2[:::med]
|
|
74
|
+
ClassB -->|dml_in_loop: high| Risk3[:::hi]
|
|
59
75
|
classDef hi fill:#fcc,stroke:#900
|
|
60
76
|
classDef med fill:#ffe6cc,stroke:#c60
|
|
61
77
|
```
|
|
@@ -64,6 +80,8 @@ If the scan returns >50 findings, render only the top-10-by-fan-in row and link
|
|
|
64
80
|
|
|
65
81
|
## Staleness check
|
|
66
82
|
|
|
83
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
84
|
+
|
|
67
85
|
Before calling `governor_risk_check`, invoke `staleness_check` for the target org. If the report says stale, surface a warning to the user:
|
|
68
86
|
|
|
69
87
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -81,6 +99,6 @@ Then continue with the playbook.
|
|
|
81
99
|
|
|
82
100
|
- **Never auto-apply fixes.** This skill does not edit Apex.
|
|
83
101
|
- Do not flatten severities — the bucket ordering matters.
|
|
84
|
-
- Do not invent additional rules; only report what `governor_risk_check` returns (
|
|
102
|
+
- Do not invent additional rules; only report what `governor_risk_check` returns (the six kinds above).
|
|
85
103
|
- Do not report "no governor risks" without first checking staleness — an empty result on a pre-AST-parser ingest means the graph lacks `inLoop` tags, not that the code is clean. Recommend a re-ingest.
|
|
86
104
|
- Do not silently exclude managed-package code; mention it was filtered out if so.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sf-graph-plus-source
|
|
3
|
+
description: Use when you have file access AND sfgraph tools and the question is about references, flows, field usage, downstream reach or similar code. The graph gives the fast, complete-looking answer; this skill says what to double-check in the source before you finish, so graph speed and your own reading both count.
|
|
4
|
+
triggers:
|
|
5
|
+
- "where is this used"
|
|
6
|
+
- "trace downstream"
|
|
7
|
+
- "analyze field"
|
|
8
|
+
- "cross layer"
|
|
9
|
+
- "find similar"
|
|
10
|
+
tools_used:
|
|
11
|
+
- analyze_field
|
|
12
|
+
- cross_layer_flow_map
|
|
13
|
+
- trace_downstream
|
|
14
|
+
- find_similar
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# sf-graph-plus-source
|
|
18
|
+
|
|
19
|
+
The graph is the starting point, not the last word. Call the tool first, then spend a few file reads on the gaps below. Do not re-derive what the graph already returned.
|
|
20
|
+
|
|
21
|
+
## Recipe: analyze_field / who reads or writes a field
|
|
22
|
+
1. `analyze_field` for the field. Keep every component it lists.
|
|
23
|
+
2. Grep the source once for the field's API name (`Field__c`). Add a component only if you see the name written in its source (Apex, Flow, LWC, Vlocity JSON). Skip profiles, layouts and permission sets.
|
|
24
|
+
|
|
25
|
+
## Recipe: cross_layer_flow_map
|
|
26
|
+
1. Call `cross_layer_flow_map` and keep its whole chain.
|
|
27
|
+
2. Check each hop that crosses a layer boundary (LWC to Apex, Apex to Flow, Flow to Apex, OmniScript to Integration Procedure to DataRaptor). Grep for the hop's name; add a missing hop only when its source names the next component.
|
|
28
|
+
|
|
29
|
+
## Recipe: trace_downstream
|
|
30
|
+
1. Call `trace_downstream`. It is usually complete, and extras are the common error here.
|
|
31
|
+
2. Do not add components from a grep. Only remove or flag one when the source shows it is only mentioned in a comment or string that is not a real reference. Remember objects, fields, validation rules and named credentials are leaves.
|
|
32
|
+
|
|
33
|
+
## Recipe: find_similar
|
|
34
|
+
1. Call `find_similar`. Open the top few hits and keep only those that do the same job (same inputs and effect), not just similar names.
|
|
35
|
+
|
|
36
|
+
## Complete the set, then verify each layer
|
|
37
|
+
Before you answer a "list every ..." question:
|
|
38
|
+
1. Write the candidate list, each with its source: the tool, a grep hit, or a file you read.
|
|
39
|
+
2. Go layer by layer (UI, Apex, Flow, OmniStudio, objects and fields) and confirm each candidate in that layer, or drop it with a reason.
|
|
40
|
+
3. Compare your count with the count the tool states (the "Found N" line, the trace total, any "N hidden" note). If they differ, name the difference.
|
|
41
|
+
4. Names built in strings (`'Obj__c'`, `put('Field__c', ...)`, a class name in a variable) can be missing from the graph. Grep for the name before you call the list complete.
|
|
42
|
+
|
|
43
|
+
## Always
|
|
44
|
+
- Say which parts came from the graph and which you confirmed in source.
|
|
45
|
+
- If the two disagree, trust the source you actually read and say so in a short sentence.
|
|
46
|
+
- Keep the answer plain: lead with the finding, then one short line per component.
|
|
@@ -29,6 +29,10 @@ the antidote to the #1 failure of LLM-assisted Salesforce work: **hallucinating
|
|
|
29
29
|
names, classes, and call chains that don't exist in this org.** This skill decides which
|
|
30
30
|
specialised skill/tool to use, and enforces one rule above all.
|
|
31
31
|
|
|
32
|
+
## Evidence rule — say it only if something backs it
|
|
33
|
+
|
|
34
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
35
|
+
|
|
32
36
|
## The grounding-first rule (non-negotiable)
|
|
33
37
|
|
|
34
38
|
**Before you generate or modify any Salesforce metadata — Apex, LWC, Flow, validation
|
|
@@ -48,9 +52,40 @@ If `staleness_check` says the ingest is old, say so — your grounding may be ou
|
|
|
48
52
|
and recommend `sfgraph ingest --org <alias>`. If the org isn't ingested at all, say the
|
|
49
53
|
grounding step is unavailable and proceed with explicit low-confidence caveats.
|
|
50
54
|
|
|
51
|
-
Only after grounding do you hand off to a design/knowledge skill (`sf-architect
|
|
55
|
+
Only after grounding do you hand off to a design/knowledge skill (`sf-architect-*`, when installed; it is not part of sfgraph, so design directly if it is missing) to
|
|
52
56
|
write the code. Ground → design → verify. Never design → hope.
|
|
53
57
|
|
|
58
|
+
## When the graph doesn't hold what the analysis needs — pull it from the org
|
|
59
|
+
|
|
60
|
+
The graph is **metadata structure**, not source code or record data. When an analysis
|
|
61
|
+
genuinely needs something the graph doesn't store, fetch it — don't guess and don't analyse
|
|
62
|
+
a stub. Two cases, with very different guardrails:
|
|
63
|
+
|
|
64
|
+
- **Source code & metadata definitions (fetch freely).** The graph stores source for Apex
|
|
65
|
+
methods (`ApexMethod:…`), trigger bodies (`ApexTrigger:…`), and Flow definitions
|
|
66
|
+
(`Flow:…`) — but NOT whole classes, LWC JS, or managed/`(hidden)` code, and some metadata
|
|
67
|
+
isn't ingested. Get the real definition when you need more: read the
|
|
68
|
+
**local sfdx file** (`force-app/main/default/{classes,triggers,flows,objects}/…`) first, else
|
|
69
|
+
retrieve from the org (`sf project retrieve start -m "ApexTrigger:<Name>"` / `Flow:<Name>` /
|
|
70
|
+
`CustomObject:<Name>`, or Tooling `SELECT Body FROM ApexClass WHERE Name='…'`).
|
|
71
|
+
- **CONFIG / metadata RECORDS (fetch when needed).** Custom Metadata records, Custom Setting
|
|
72
|
+
rows, picklist/record-type values, Named Credential config — these are deploy-time config,
|
|
73
|
+
not customer data. Pull them with `sf data query -q "SELECT … FROM <Type>__mdt"` (or the
|
|
74
|
+
setting `__c`, or Tooling for record types/picklists) when a trace/debug needs the value the
|
|
75
|
+
config resolves to. (1.4.0+ ingests CMDT/label/setting values into the graph, but older
|
|
76
|
+
graphs or un-ingested types may lack them — fetch then.)
|
|
77
|
+
- **BUSINESS SObject records (the bounded EXCEPTION — pull minimally, with care).** sfgraph is
|
|
78
|
+
deliberately **local-first and metadata-only**; actual records (Account/Contact/Opportunity
|
|
79
|
+
rows, etc.) are out of the graph by design, and they are **real, possibly-PII customer data**.
|
|
80
|
+
Only when analysis truly requires it — e.g. to see a misbehaving field's real value shape or
|
|
81
|
+
confirm a data-skew assumption — pull a **small bounded sample**: always `LIMIT` (≤ a handful
|
|
82
|
+
of rows), prefer `COUNT()`/aggregates over raw rows, never bulk-dump, and **tell the user
|
|
83
|
+
you're stepping outside the local-first boundary to read live records**. If aggregates answer
|
|
84
|
+
the question, don't pull rows at all.
|
|
85
|
+
|
|
86
|
+
State which source you used (graph / local file / org metadata / live records) so the reader
|
|
87
|
+
knows the provenance and whether it left the local-only guarantee.
|
|
88
|
+
|
|
54
89
|
## Route by intent
|
|
55
90
|
|
|
56
91
|
### DEVELOPMENT — writing or changing code
|
|
@@ -60,7 +95,9 @@ write the code. Ground → design → verify. Never design → hope.
|
|
|
60
95
|
| "Build an LWC for `<feature>`" | `find_similar` (existing LWCs) + `analyze_field` (bound fields) | `sf-architect-ui` |
|
|
61
96
|
| "Add an integration / callout" | `find_nodes NamedCredential:*` + `find_similar` | `sf-architect-integrations` |
|
|
62
97
|
| "Reuse — is this already built?" | `sf-find-similar` (free-text concept mode) | — |
|
|
63
|
-
| "Explain this method/
|
|
98
|
+
| "Explain this method/trigger/Flow" | `sf-explain-code` (a class: explain its methods, `ApexMethod:Class.m(n)`) | — |
|
|
99
|
+
| "Who calls / uses `<method/class>`" | `trace_upstream` on its qname (`depth: 1` for direct callers) | — |
|
|
100
|
+
| "Which tests cover `<class>`" | `trace_upstream` with `rel_types: ["IS_TEST_FOR"]` | — |
|
|
64
101
|
|
|
65
102
|
### DESIGN — architecture & data model
|
|
66
103
|
| Intent | Skill |
|
|
@@ -91,6 +128,11 @@ write the code. Ground → design → verify. Never design → hope.
|
|
|
91
128
|
| "Vlocity ↔ OmniStudio migration inventory" | `sf-omnistudio-migration-audit` |
|
|
92
129
|
| "Is the graph fresh / how do I refresh" | `sf-metadata-refresh` |
|
|
93
130
|
| "Show me the graph visually" | `sf-web-explorer` |
|
|
131
|
+
| "Export findings for CI / code scanning (SARIF)" | `export_sarif` |
|
|
132
|
+
|
|
133
|
+
When you also have the project's source files, follow **`sf-graph-plus-source`** after the tool call: it says which parts of a graph answer to double-check in the source.
|
|
134
|
+
|
|
135
|
+
Most skills start with `staleness_check`. With `SFGRAPH_TOOLS=core` the server does not offer it (nor `list_orgs`, `snapshot_*` or `freshness_report`): skip those steps and say the graph's freshness was not checked.
|
|
94
136
|
|
|
95
137
|
## Disambiguation (commonly confused)
|
|
96
138
|
- **Committed vs uncommitted vs post-deploy change analysis:** PR/branch/commit → `sf-impact-from-diff`; uncommitted working tree → `sf-wip-impact`; already deployed, something regressed → `sf-what-broke`.
|
|
@@ -16,13 +16,17 @@ tools_used:
|
|
|
16
16
|
|
|
17
17
|
# sf-impact-from-diff
|
|
18
18
|
|
|
19
|
-
Use when the user wants to understand the downstream consequences of a
|
|
19
|
+
Use when the user wants to understand the downstream consequences of committed changes (a PR, a branch or a commit range) before merging. For uncommitted or staged work, use **sf-wip-impact**, which reads the working tree itself.
|
|
20
|
+
|
|
21
|
+
## Evidence rule — say it only if something backs it
|
|
22
|
+
|
|
23
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
20
24
|
|
|
21
25
|
## Playbook
|
|
22
26
|
|
|
23
|
-
1.
|
|
24
|
-
2. Call `impact_from_git_diff
|
|
25
|
-
3. Call `test_gap_intelligence_from_git_diff`
|
|
27
|
+
1. Get the diff text yourself: the tools take the unified diff as their `diff` argument and do not run git. Confirm the range with the user (a PR's base and head, a branch, or a commit range); if unspecified, use `git diff origin/main...HEAD`.
|
|
28
|
+
2. Call `impact_from_git_diff(diff=…, depth=…)`. Expect a list of changed metadata nodes plus their downstream dependents grouped by edge type (`READS_FIELD`, `CALLS`, `FLOW_INVOKES_APEX`, `EMBEDS_LWC`, …).
|
|
29
|
+
3. Call `test_gap_intelligence_from_git_diff` with the same `diff` text. Cross-reference impacted nodes against existing Apex test coverage and surface the uncovered set.
|
|
26
30
|
4. Group findings by risk tier — **direct edits**, **first-degree dependents**, **transitive (depth ≥ 2)** — and call out anything that crosses metadata layers (LWC -> Apex -> Flow).
|
|
27
31
|
5. Render the Mermaid impact graph returned by the tool. If the impact set is large (>40 nodes), summarise by category and offer a follow-up call scoped to a single changed file.
|
|
28
32
|
6. If test gaps exist, list the specific impacted classes + methods that lack assertions, not just totals.
|
|
@@ -30,7 +34,7 @@ Use when the user wants to understand the downstream consequences of a pending c
|
|
|
30
34
|
|
|
31
35
|
## Visualization
|
|
32
36
|
|
|
33
|
-
Render a **`flowchart LR`** impact graph. Changed nodes are leftmost (highlighted), first-degree dependents in the middle, transitive (depth >= 2) on the right. Edge style encodes edge type (`
|
|
37
|
+
Render a **`flowchart LR`** impact graph. Changed nodes are leftmost (highlighted), first-degree dependents in the middle, transitive (depth >= 2) on the right. Edge style encodes edge type (`CALLS`, `READS_FIELD`, `FLOW_INVOKES_APEX`, `EMBEDS_LWC`).
|
|
34
38
|
|
|
35
39
|
```
|
|
36
40
|
flowchart LR
|
|
@@ -44,6 +48,8 @@ When the impact set exceeds 40 nodes, summarise per category and offer the user
|
|
|
44
48
|
|
|
45
49
|
## Staleness check
|
|
46
50
|
|
|
51
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
52
|
+
|
|
47
53
|
Before calling `impact_from_git_diff`, invoke `staleness_check` for the linked org. A stale graph will under-report dependents added since the last sync. If the report says stale, surface a warning to the user:
|
|
48
54
|
|
|
49
55
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -18,12 +18,16 @@ tools_used:
|
|
|
18
18
|
|
|
19
19
|
Use when the user asks whether their sfgraph data is current, how to refresh it, or wants to see both graph staleness and org-side dead-metadata freshness.
|
|
20
20
|
|
|
21
|
+
## Evidence rule — say it only if something backs it
|
|
22
|
+
|
|
23
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
24
|
+
|
|
21
25
|
## Playbook
|
|
22
26
|
|
|
23
27
|
1. Call `staleness_check` for the target org. Report the exact age in days and whether it's stale.
|
|
24
|
-
2. If stale, call `start_ingest_job
|
|
25
|
-
|
|
26
|
-
Surface that command verbatim to the user. Do not promise the MCP server
|
|
28
|
+
2. If stale, call `start_ingest_job`. It does NOT run the ingest: its report says
|
|
29
|
+
nothing was started and shows the command to run (for example
|
|
30
|
+
`sfgraph ingest --org <alias>`). Surface that command verbatim to the user. Do not promise the MCP server
|
|
27
31
|
will run it. After the user has executed the ingest in a shell, MCP tools
|
|
28
32
|
will see the new data on their next invocation.
|
|
29
33
|
3. Suggest the right shape of the command for their situation:
|
|
@@ -54,6 +58,8 @@ Pair it with the bucketed freshness table from `freshness_report` for the org-si
|
|
|
54
58
|
|
|
55
59
|
## Staleness check
|
|
56
60
|
|
|
61
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
62
|
+
|
|
57
63
|
`staleness_check` _is_ the first step here — the whole skill is the staleness check. If `staleness_check` returns `stale=false`, still continue to `freshness_report` so the user sees the org-side picture.
|
|
58
64
|
|
|
59
65
|
## Response Shape
|
|
@@ -8,6 +8,8 @@ triggers:
|
|
|
8
8
|
- "omnistudio audit"
|
|
9
9
|
- "migrate from vlocity"
|
|
10
10
|
tools_used:
|
|
11
|
+
- find_nodes
|
|
12
|
+
- trace_downstream
|
|
11
13
|
- cross_org_diff
|
|
12
14
|
- staleness_check
|
|
13
15
|
---
|
|
@@ -16,11 +18,17 @@ tools_used:
|
|
|
16
18
|
|
|
17
19
|
Use when the user is mid-migration from Vlocity CMT (managed package) to OmniStudio on Core and wants to verify parity by category.
|
|
18
20
|
|
|
21
|
+
## Evidence rule — say it only if something backs it
|
|
22
|
+
|
|
23
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
24
|
+
|
|
19
25
|
## Playbook
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
No tool computes these counts; you build them from `find_nodes` and `trace_downstream` results, and say so. The graph labels are the same whatever the org's namespace (there is no `vlocity_cmt__` prefix on a label).
|
|
28
|
+
|
|
29
|
+
1. List the Vlocity-CMT assets with `find_nodes(label=…)` for each of `DataRaptor`, `IntegrationProcedure`, `OmniScript` and `VlocityCard`. Raise `limit` until the result is not truncated.
|
|
30
|
+
2. List the OmniStudio-on-Core twins the same way: `OmniDataTransform`, `OmniIntegrationProcedure`, `OmniProcess` (OmniScript) and `OmniUiCard`. The pairs are DataRaptor ↔ OmniDataTransform, IntegrationProcedure ↔ OmniIntegrationProcedure, OmniScript ↔ OmniProcess, VlocityCard ↔ OmniUiCard.
|
|
31
|
+
3. For each CMT asset, `trace_downstream(qname, depth=1, rel_types=["CANONICAL_OF"])` finds its twin (the edge is written both ways when the names match). With many assets, check a sample and say so. Per pair of labels, report the CMT count, the Core count, the mirrored pairs, and the unmirrored assets on each side.
|
|
24
32
|
4. If the user provided two orgs (typically a CMT-only sandbox vs an OmniStudio-on-Core sandbox), call `cross_org_diff` to highlight assets present in one and not the other.
|
|
25
33
|
5. Bucket findings:
|
|
26
34
|
- **fully migrated** — pair exists, `CANONICAL_OF` edge present, no logic drift.
|
|
@@ -35,13 +43,13 @@ Render a **`flowchart LR`** parity diagram. Left subgraph is Vlocity-CMT; right
|
|
|
35
43
|
```
|
|
36
44
|
flowchart LR
|
|
37
45
|
subgraph CMT
|
|
38
|
-
D1[
|
|
46
|
+
D1[DataRaptor:Foo]
|
|
39
47
|
end
|
|
40
48
|
subgraph Core
|
|
41
49
|
D2[OmniDataTransform:Foo]
|
|
42
50
|
end
|
|
43
51
|
D1 -. CANONICAL_OF .-> D2
|
|
44
|
-
D3[
|
|
52
|
+
D3[OmniScript:Bar]:::unmirrored
|
|
45
53
|
classDef unmirrored stroke-dasharray:4 4,fill:#fdd
|
|
46
54
|
```
|
|
47
55
|
|
|
@@ -49,6 +57,8 @@ For orgs with >50 CMT assets, swap the diagram for a per-category counts table
|
|
|
49
57
|
|
|
50
58
|
## Staleness check
|
|
51
59
|
|
|
60
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
61
|
+
|
|
52
62
|
Before running the audit, invoke `staleness_check` for each org in scope. A stale ingest means recently-created CANONICAL_OF edges or new OmniStudio twins are missing, which inflates the "not started" bucket. If the report says stale, surface a warning to the user:
|
|
53
63
|
|
|
54
64
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -19,14 +19,18 @@ tools_used:
|
|
|
19
19
|
|
|
20
20
|
Use when the user wants a high-level picture of the data model — which objects exist in a domain, how they connect via Lookup/MasterDetail, and which Apex/Flow/LWC artifacts depend on them.
|
|
21
21
|
|
|
22
|
+
## Evidence rule — say it only if something backs it
|
|
23
|
+
|
|
24
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
25
|
+
|
|
22
26
|
## Playbook
|
|
23
27
|
|
|
24
28
|
1. Identify the focal object(s) from the user's question. If they named a domain ("the Order schema"), pick the lead object and include its first-degree neighbours.
|
|
25
29
|
2. Call `staleness_check` for the org. Warn if stale.
|
|
26
|
-
3. For each focal object, call `trace_downstream`
|
|
30
|
+
3. For each focal object, call `trace_downstream(qname="CustomObject:<name>", depth=2, rel_types=["DEFINES_FIELD", "REFERENCES_OBJECT"])`. A relationship shows up as two hops: the object defines a lookup field, and the field references the target object (`CustomObject:Invoice__c` → `CustomField:Invoice__c.Account__c` → `CustomObject:Account`). For relationships pointing **into** an object, call `trace_upstream(qname="CustomObject:<name>", depth=1, rel_types=["REFERENCES_OBJECT"])`: it lists the lookup fields on other objects.
|
|
27
31
|
4. Call `trace_upstream` for the same object to find what depends on it (Apex selectors, Flows, LWCs, layouts).
|
|
28
32
|
5. If the user named a specific field, call `analyze_field` to get its readers/writers and a precise role description.
|
|
29
|
-
6. Produce an ERD-like diagram with one entity per object and relationship
|
|
33
|
+
6. Produce an ERD-like diagram with one entity per object and one relationship line per lookup field. The report does not say whether a relationship is a Lookup or a Master-Detail: read the field's metadata (`objects/<Object>/fields/<Field>.field-meta.xml`, `<type>`) when the cardinality matters, or draw it as a plain relationship and say so.
|
|
30
34
|
|
|
31
35
|
## Visualization
|
|
32
36
|
|
|
@@ -43,6 +47,8 @@ When the schema has >20 entities, prune to the focal object's two-hop neighbourh
|
|
|
43
47
|
|
|
44
48
|
## Staleness check
|
|
45
49
|
|
|
50
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
51
|
+
|
|
46
52
|
Before tracing, invoke `staleness_check` for the org. New custom objects/fields and recently-added relationships are invisible in a stale graph. If the report says stale, surface a warning to the user:
|
|
47
53
|
|
|
48
54
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -18,16 +18,20 @@ tools_used:
|
|
|
18
18
|
|
|
19
19
|
Use when the user asks about who can read or write a field/object, what sharing rules apply, or whether FLS / CRUD is consistent with intent.
|
|
20
20
|
|
|
21
|
+
## Evidence rule — say it only if something backs it
|
|
22
|
+
|
|
23
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
24
|
+
|
|
21
25
|
## Playbook
|
|
22
26
|
|
|
23
27
|
1. Resolve the target. Call `analyze_field` if the user gave `SObject.Field`; otherwise treat as SObject-level. `analyze_field` takes **separate `object` and `field`** params (not a single `Account.Status` string) — split before calling.
|
|
24
28
|
2. Call `security_audit` with the resolved target (`object` / `field` to narrow; whole-org if neither). Capture full-access sharing rules and the FLS gap list.
|
|
25
29
|
- **FLS gaps are pre-filtered** to FLS-relevant fields: Custom Metadata Type (`__mdt`), platform-event (`__e`), big-object (`__b`), and standard system/audit fields (Id, CreatedById, SystemModstamp, …) are excluded as noise. The response notes `+N CMDT/system fields hidden`. Only pass `include_non_fls_fields: true` if the user explicitly wants that raw set — never lead with it.
|
|
26
30
|
- The inline gap list is capped (`limit`, default 30). Narrow with `object`, or raise `limit`, rather than dumping thousands of rows.
|
|
27
|
-
3. Build the access matrix:
|
|
28
|
-
4.
|
|
29
|
-
5. Flag risks: `without sharing`
|
|
30
|
-
6. Render the
|
|
31
|
+
3. Build the field access matrix: for one field, the report lists every profile and permission set that grants read or edit ("Granted by"); a profile or permission set whose entry denies access is not counted. Run the audit per field you need in the matrix.
|
|
32
|
+
4. Report sharing exposure from what the tool returns: the sharing rules that give edit or full access. The tool does **not** report org-wide defaults, the role hierarchy, object CRUD per profile, or `without sharing` Apex; say so, and check those in the source or the org if the user needs them (label them as not graph-confirmed).
|
|
33
|
+
5. Flag risks you can back with evidence: full-access sharing rules and FLS gaps from the tool; `without sharing` classes, missing `stripInaccessible` / `WITH SECURITY_ENFORCED`, guest user and Experience Cloud reach only from source you read.
|
|
34
|
+
6. Render the access matrix as Mermaid yourself from the report's "Granted by" lists. The tool does not draw a diagram. See **Visualization** below.
|
|
31
35
|
|
|
32
36
|
## Visualization
|
|
33
37
|
|
|
@@ -45,6 +49,8 @@ When more than 25 Profiles/PSets have access, collapse to a count table and only
|
|
|
45
49
|
|
|
46
50
|
## Staleness check
|
|
47
51
|
|
|
52
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
53
|
+
|
|
48
54
|
Before calling `security_audit`, invoke `staleness_check` for the target org. Permission set/profile assignments change frequently; a stale graph misrepresents access. If the report says stale, surface a warning to the user:
|
|
49
55
|
|
|
50
56
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -18,6 +18,10 @@ tools_used:
|
|
|
18
18
|
|
|
19
19
|
Use when the user wants to compare two named points in time — typically a pre-deploy checkpoint vs current, or two milestone snapshots taken during a release.
|
|
20
20
|
|
|
21
|
+
## Evidence rule — say it only if something backs it
|
|
22
|
+
|
|
23
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
24
|
+
|
|
21
25
|
## Playbook
|
|
22
26
|
|
|
23
27
|
1. Call `snapshot_list` for the org. Present available snapshots with their `id`, `label`, `createdAt`, and `kind` (auto/manual).
|
|
@@ -45,6 +49,8 @@ If `gitGraph` isn't supported by the client, fall back to a small **`flowchart L
|
|
|
45
49
|
|
|
46
50
|
## Staleness check
|
|
47
51
|
|
|
52
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
53
|
+
|
|
48
54
|
Before calling `point_in_time_diff` with `to='current'`, invoke `staleness_check` for the org. If the report says stale, surface a warning to the user:
|
|
49
55
|
|
|
50
56
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -7,7 +7,8 @@ triggers:
|
|
|
7
7
|
- "open the web view"
|
|
8
8
|
- "see the relationships visually"
|
|
9
9
|
- "render the whole org"
|
|
10
|
-
tools_used:
|
|
10
|
+
tools_used:
|
|
11
|
+
- staleness_check
|
|
11
12
|
when_to_use: Visual exploration of an ingested org. Pairs with `analyze_field`, `trace_upstream`, `trace_downstream`, and `cross_layer_flow_map` when the markdown answers are not enough and the user wants to click around.
|
|
12
13
|
---
|
|
13
14
|
|
|
@@ -17,6 +18,10 @@ The MCP tool surface is great for answering specific questions, but sometimes th
|
|
|
17
18
|
|
|
18
19
|
This skill does not call any MCP tool. It instructs the user how to launch the viewer and what to do once it's open.
|
|
19
20
|
|
|
21
|
+
## Evidence rule — say it only if something backs it
|
|
22
|
+
|
|
23
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
24
|
+
|
|
20
25
|
## Playbook
|
|
21
26
|
|
|
22
27
|
1. Confirm the org has been ingested (use `staleness_check` if uncertain).
|
|
@@ -15,33 +15,39 @@ tools_used:
|
|
|
15
15
|
|
|
16
16
|
# sf-what-broke
|
|
17
17
|
|
|
18
|
-
Use when the user is triaging a regression and wants to know which metadata changed
|
|
18
|
+
Use when the user is triaging a regression and wants to know which metadata changed since a recent deploy/sync, and which of those changes have **untested dependents** (the likely break surface).
|
|
19
|
+
|
|
20
|
+
**What the tool actually does (and doesn't):** `what_broke(org, since?)` compares the current graph against the latest pre-sync snapshot (or the `since` you pass), finds the changed nodes, and buckets their dependents as **at-risk** (no test coverage) vs **covered**. It does NOT take a failing-test/stack "failure signal", does NOT rank by reachability to a specific failure, and does NOT emit a path-to-failure diagram. If the user has a concrete failure (a specific exception or failing test), route that to **`sf-debug-root-cause`** (symptom → graph cause) or **`sf-debug-log-analysis`** — then come back here for the "what changed in the window" picture.
|
|
21
|
+
|
|
22
|
+
## Evidence rule — say it only if something backs it
|
|
23
|
+
|
|
24
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
19
25
|
|
|
20
26
|
## Playbook
|
|
21
27
|
|
|
22
|
-
1.
|
|
23
|
-
2. Call `
|
|
24
|
-
3.
|
|
25
|
-
4. Triage
|
|
26
|
-
5.
|
|
27
|
-
6. Recommend the smallest
|
|
28
|
+
1. **`staleness_check`** first (see below).
|
|
29
|
+
2. Call **`what_broke`** with the org (and `since` if the user has a specific baseline; otherwise it uses the latest pre-sync snapshot). If it reports no baseline snapshot, tell the user and fall back to `point_in_time_diff` / `sf-snapshot-compare` with an explicit anchor.
|
|
30
|
+
3. Read the result: the changed nodes, and their dependents split into **at-risk** (no tests — highest priority) and **covered**.
|
|
31
|
+
4. Triage at-risk first: for each, summarise what changed and what depends on it. To see the full dependency chain to a UI/test surface, hand the node to `sf-cross-layer-trace` or `trace_upstream`.
|
|
32
|
+
5. For a deeper field-level diff between two points, call **`point_in_time_diff`** (or `sf-snapshot-compare`) — that's where the detailed add/remove/modify per layer lives.
|
|
33
|
+
6. Recommend the smallest next step (revert a specific change, or add a test to an at-risk dependent before re-deploying).
|
|
28
34
|
|
|
29
35
|
## Visualization
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
`what_broke` draws a Mermaid diagram of the changed pieces and their dependents by default (`mermaid: true`; `mermaid: false` hides it). Embed the tool's diagram rather than redrawing it. If you need a different view (for example only the untested dependents), build a simple **`flowchart LR`** yourself and say you drew it.
|
|
32
38
|
|
|
33
39
|
```
|
|
34
40
|
flowchart LR
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
classDef
|
|
38
|
-
classDef
|
|
41
|
+
C1[ApexClass:AccountTrigger]:::changed --> D1[ApexClass:AccountSvc]:::atrisk
|
|
42
|
+
C1 --> D2[Flow:AccountAfterUpdate]:::atrisk
|
|
43
|
+
classDef changed fill:#fee,stroke:#c00
|
|
44
|
+
classDef atrisk fill:#fcc,stroke:#900,stroke-width:2px
|
|
39
45
|
```
|
|
40
46
|
|
|
41
|
-
If the suspect set is large (>15 nodes) collapse non-top suspects into a single "other changes" node — readability beats completeness.
|
|
42
|
-
|
|
43
47
|
## Staleness check
|
|
44
48
|
|
|
49
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
50
|
+
|
|
45
51
|
Before calling `what_broke`, invoke `staleness_check` for the target org. If the report says stale, surface a warning to the user:
|
|
46
52
|
|
|
47
53
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -50,15 +56,15 @@ Then continue with the playbook.
|
|
|
50
56
|
|
|
51
57
|
## Response Shape
|
|
52
58
|
|
|
53
|
-
- **Window** —
|
|
54
|
-
- **
|
|
55
|
-
- **
|
|
56
|
-
- **
|
|
57
|
-
- **Next step** — single concrete recommendation.
|
|
59
|
+
- **Window** — current vs the baseline snapshot `what_broke` used (or the `since` anchor).
|
|
60
|
+
- **At-risk changes** — changed nodes whose dependents have no test coverage, first.
|
|
61
|
+
- **Covered changes** — changed nodes whose dependents are tested, briefly.
|
|
62
|
+
- **Next step** — single concrete recommendation (revert, or add coverage to an at-risk dependent).
|
|
58
63
|
|
|
59
64
|
## Don't
|
|
60
65
|
|
|
61
|
-
- Do not
|
|
66
|
+
- Do not tell the user you passed a failing test / stack to `what_broke` — it has no such parameter. Route concrete failures to `sf-debug-root-cause`.
|
|
67
|
+
- `what_broke` DOES emit a dependency-graph Mermaid alongside its tables — embed the tool's diagram directly; don't claim it returns tables only, and don't redraw it from scratch.
|
|
68
|
+
- Do not propose a code fix without first inspecting the suspect change via `sf-cross-layer-trace`.
|
|
62
69
|
- Do not run write operations against the org. This skill only reads the local graph + snapshots.
|
|
63
|
-
- Do not
|
|
64
|
-
- Do not invent snapshots that don't exist; if the "good" anchor is unavailable, say so.
|
|
70
|
+
- Do not invent snapshots that don't exist; if no baseline is available, say so and use an explicit anchor.
|
|
@@ -18,6 +18,12 @@ tools_used:
|
|
|
18
18
|
|
|
19
19
|
Use when the user has local edits they haven't committed yet (working tree, staged or unstaged) and wants to see the downstream blast radius and test gaps before deciding whether to commit or push.
|
|
20
20
|
|
|
21
|
+
What counts as changed: the wip tools compare the local project with the ingested graph. In the default `mode: "changed-only"`, a file counts when it differs from git HEAD **or** was modified after the org's last sync, so commits made since the last ingest are included too; say so if the user expects only uncommitted edits. `mode: "full-folder"` parses every member.
|
|
22
|
+
|
|
23
|
+
## Evidence rule — say it only if something backs it
|
|
24
|
+
|
|
25
|
+
State a fact about this org or the Salesforce platform **only when evidence backs it**: a tool/graph result, source you actually read (local sfdx file or an org fetch), a live org query, or official Salesforce documentation. If you don't have that, say so plainly — "the graph doesn't show this", "unverified", "I'd need to check the org" — and either go get the evidence or stop. **Never fill the gap with a plausible-sounding guess**: an invented field/method/object name, an assumed dependency, or a governor number recalled from memory. Label each claim as **graph-confirmed** (a tool returned it), **inferred** (you reasoned it from graph facts — say which facts), or **general Salesforce knowledge**. For platform behaviour — governor limits, order of execution, sharing/FLS semantics, API rules — cite the official doc (developer.salesforce.com; fetch it if unsure) instead of asserting from memory. If the graph is stale or the org was never ingested, lead with that caveat — your grounding may be wrong.
|
|
26
|
+
|
|
21
27
|
## Playbook
|
|
22
28
|
|
|
23
29
|
1. Confirm the project root. Default to the current working directory; if the user explicitly names another, use that.
|
|
@@ -52,6 +58,8 @@ If the dependent set exceeds 40 nodes, summarise per category and skip the diagr
|
|
|
52
58
|
|
|
53
59
|
## Staleness check
|
|
54
60
|
|
|
61
|
+
If `staleness_check` is not available (the server runs with `SFGRAPH_TOOLS=core`), skip this step and say the graph's freshness was not checked.
|
|
62
|
+
|
|
55
63
|
Before calling `wip_impact`, invoke `staleness_check` for the linked org. WIP analysis depends on the existing graph being accurate; a stale graph will under-report dependents. If the report says stale, surface a warning to the user:
|
|
56
64
|
|
|
57
65
|
> Your ingest is N days old. Run `sfgraph ingest --org <alias>` to refresh, or proceed with the understanding that the graph may not reflect recent changes.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-format.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cursor-format.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { toClaudeFormat, toCursorFormat } from "../installer.js";
|
|
3
|
-
const SAMPLE = `---
|
|
4
|
-
name: foo
|
|
5
|
-
description: bar
|
|
6
|
-
triggers:
|
|
7
|
-
- "x"
|
|
8
|
-
tools_used:
|
|
9
|
-
- tool_a
|
|
10
|
-
- tool_b
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Body
|
|
14
|
-
|
|
15
|
-
Some content.
|
|
16
|
-
|
|
17
|
-
- bullet
|
|
18
|
-
`;
|
|
19
|
-
describe("format transforms", () => {
|
|
20
|
-
it("toCursorFormat rewrites frontmatter into Cursor's contract", () => {
|
|
21
|
-
const out = toCursorFormat(SAMPLE);
|
|
22
|
-
// Cursor-shaped frontmatter (the only three keys it understands)
|
|
23
|
-
expect(out).toMatch(/^description:.*bar/m);
|
|
24
|
-
// globs now contains the SF file patterns so the rule auto-attaches
|
|
25
|
-
// whenever the user opens any Salesforce metadata file.
|
|
26
|
-
expect(out).toMatch(/^globs:.*sfdx-project\.json.*\*\*\/\*\.cls/m);
|
|
27
|
-
expect(out).toMatch(/^alwaysApply:\s*false$/m);
|
|
28
|
-
// Trigger phrases folded into the description so the agent matches them
|
|
29
|
-
expect(out).toMatch(/Triggers:.*"?x"?/);
|
|
30
|
-
// The agent-requested hint mentions the skill name + sfgraph routing
|
|
31
|
-
expect(out).toMatch(/sfgraph MCP tools/);
|
|
32
|
-
// Body preserved verbatim
|
|
33
|
-
expect(out).toMatch(/^# Body/m);
|
|
34
|
-
expect(out).toMatch(/^- bullet/m);
|
|
35
|
-
// Old frontmatter is gone (it would confuse Cursor's parser)
|
|
36
|
-
expect(out).not.toMatch(/^name: foo/m);
|
|
37
|
-
expect(out).not.toMatch(/^triggers:/m);
|
|
38
|
-
expect(out).not.toMatch(/^tools_used:/m);
|
|
39
|
-
});
|
|
40
|
-
it("toClaudeFormat is identity", () => {
|
|
41
|
-
expect(toClaudeFormat(SAMPLE)).toBe(SAMPLE);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
//# sourceMappingURL=cursor-format.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-format.test.js","sourceRoot":"","sources":["../../src/__tests__/cursor-format.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjE,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;CAed,CAAC;AAEF,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACnC,iEAAiE;QACjE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;QAC3C,oEAAoE;QACpE,wDAAwD;QACxD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,6CAA6C,CAAC,CAAC;QACnE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC;QAC/C,wEAAwE;QACxE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QACxC,qEAAqE;QACrE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QACzC,0BAA0B;QAC1B,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAClC,6DAA6D;QAC7D,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"installer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/installer.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
-
import { install, listSkillsBundled } from "../installer.js";
|
|
6
|
-
let home;
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
home = mkdtempSync(join(tmpdir(), "sfgraph-home-"));
|
|
9
|
-
});
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
rmSync(home, { recursive: true, force: true });
|
|
12
|
-
});
|
|
13
|
-
describe("skills installer", () => {
|
|
14
|
-
it("installs all 20 skills to the claude target", async () => {
|
|
15
|
-
const results = await install("claude", { homeOverride: home });
|
|
16
|
-
const skillNames = await listSkillsBundled();
|
|
17
|
-
expect(skillNames.length).toBe(20);
|
|
18
|
-
expect(results.length).toBe(20);
|
|
19
|
-
for (const r of results) {
|
|
20
|
-
expect(r.target).toBe("claude");
|
|
21
|
-
expect(r.action).toBe("created");
|
|
22
|
-
expect(existsSync(r.path)).toBe(true);
|
|
23
|
-
expect(r.path).toContain(join(home, ".claude", "skills"));
|
|
24
|
-
expect(r.path.endsWith("SKILL.md")).toBe(true);
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
it("installs to the cursor target as .mdc files in Cursor's frontmatter shape", async () => {
|
|
28
|
-
const results = await install("cursor", { homeOverride: home });
|
|
29
|
-
expect(results.length).toBe(20);
|
|
30
|
-
const cursorDir = join(home, ".cursor", "rules");
|
|
31
|
-
const files = readdirSync(cursorDir);
|
|
32
|
-
expect(files.every((f) => f.endsWith(".mdc"))).toBe(true);
|
|
33
|
-
expect(files.length).toBe(20);
|
|
34
|
-
// Cursor-shaped frontmatter (the three keys Cursor's rule loader reads).
|
|
35
|
-
const sample = readFileSync(join(cursorDir, files[0] ?? ""), "utf8");
|
|
36
|
-
expect(sample).toMatch(/^description:/m);
|
|
37
|
-
expect(sample).toMatch(/^globs:.*\*\*\/\*\.cls/m);
|
|
38
|
-
expect(sample).toMatch(/^alwaysApply:\s*false$/m);
|
|
39
|
-
// Old frontmatter is rewritten away (would confuse Cursor's parser).
|
|
40
|
-
expect(sample).not.toMatch(/^name:/m);
|
|
41
|
-
expect(sample).not.toMatch(/^triggers:/m);
|
|
42
|
-
expect(sample).not.toMatch(/^tools_used:/m);
|
|
43
|
-
// Body content preserved.
|
|
44
|
-
expect(sample).toMatch(/## Playbook/);
|
|
45
|
-
});
|
|
46
|
-
it("installs to all targets in one call", async () => {
|
|
47
|
-
const results = await install("all", { homeOverride: home });
|
|
48
|
-
const claudeCount = results.filter((r) => r.target === "claude").length;
|
|
49
|
-
const cursorCount = results.filter((r) => r.target === "cursor").length;
|
|
50
|
-
const vscodeCount = results.filter((r) => r.target === "vscode").length;
|
|
51
|
-
expect(claudeCount).toBe(20);
|
|
52
|
-
expect(cursorCount).toBe(20);
|
|
53
|
-
expect(vscodeCount).toBe(20);
|
|
54
|
-
expect(existsSync(join(home, ".claude", "skills"))).toBe(true);
|
|
55
|
-
expect(existsSync(join(home, ".cursor", "rules"))).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
it("dry-run returns would-write actions and writes nothing", async () => {
|
|
58
|
-
const results = await install("all", { homeOverride: home, dryRun: true });
|
|
59
|
-
expect(results.every((r) => r.action === "would-write")).toBe(true);
|
|
60
|
-
expect(existsSync(join(home, ".claude", "skills"))).toBe(false);
|
|
61
|
-
expect(existsSync(join(home, ".cursor", "rules"))).toBe(false);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
//# sourceMappingURL=installer.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"installer.test.js","sourceRoot":"","sources":["../../src/__tests__/installer.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE7D,IAAI,IAAY,CAAC;AAEjB,UAAU,CAAC,GAAG,EAAE;IACd,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AACH,SAAS,CAAC,GAAG,EAAE;IACb,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,MAAM,UAAU,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAC7C,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACjC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC1D,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,yEAAyE;QACzE,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC;QAClD,qEAAqE;QACrE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC5C,0BAA0B;QAC1B,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;QACxE,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;QACxE,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;QACxE,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3E,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|