@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.
Files changed (30) hide show
  1. package/package.json +3 -2
  2. package/skills/sf-cross-layer-trace/SKILL.md +12 -5
  3. package/skills/sf-cross-org-diff/SKILL.md +7 -1
  4. package/skills/sf-dead-code-audit/SKILL.md +10 -3
  5. package/skills/sf-debug-log-analysis/SKILL.md +6 -2
  6. package/skills/sf-debug-root-cause/SKILL.md +17 -2
  7. package/skills/sf-deployment-manifest/SKILL.md +13 -7
  8. package/skills/sf-explain-code/SKILL.md +16 -2
  9. package/skills/sf-find-similar/SKILL.md +10 -4
  10. package/skills/sf-flow-impact/SKILL.md +6 -0
  11. package/skills/sf-governor-risk-fix/SKILL.md +35 -17
  12. package/skills/sf-graph-plus-source/SKILL.md +46 -0
  13. package/skills/sf-graph-router/SKILL.md +44 -2
  14. package/skills/sf-impact-from-diff/SKILL.md +11 -5
  15. package/skills/sf-metadata-refresh/SKILL.md +9 -3
  16. package/skills/sf-omnistudio-migration-audit/SKILL.md +15 -5
  17. package/skills/sf-schema-overview/SKILL.md +8 -2
  18. package/skills/sf-security-audit/SKILL.md +10 -4
  19. package/skills/sf-snapshot-compare/SKILL.md +6 -0
  20. package/skills/sf-web-explorer/SKILL.md +6 -1
  21. package/skills/sf-what-broke/SKILL.md +28 -22
  22. package/skills/sf-wip-impact/SKILL.md +8 -0
  23. package/dist/__tests__/cursor-format.test.d.ts +0 -2
  24. package/dist/__tests__/cursor-format.test.d.ts.map +0 -1
  25. package/dist/__tests__/cursor-format.test.js +0 -44
  26. package/dist/__tests__/cursor-format.test.js.map +0 -1
  27. package/dist/__tests__/installer.test.d.ts +0 -2
  28. package/dist/__tests__/installer.test.d.ts.map +0 -1
  29. package/dist/__tests__/installer.test.js +0 -64
  30. 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.2.0",
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": ">=20"
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. If the user gave a field, call `analyze_field` first to canonicalise `SObject.Field` and gather direct readers/writers.
41
- 2. Call `cross_layer_flow_map` with the resolved node. Receive ordered paths across layers (UI -> Controller -> Service -> Selector -> SObject and any Flow / Process Builder branches).
42
- 3. For each distinct path, summarise the layer transitions with the edge type that bridges them (`@wire`, `@AuraEnabled`, `READS_FIELD`, `UPDATES_FIELD`, `INVOKES_FLOW`).
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`, `UPDATES_FIELD`, `INVOKES_FLOW`).
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
- - Do not include nodes outside the requested traversal depth; defer breadth to a follow-up call.
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(orgA, orgB)`. Capture added / removed / modified nodes grouped by metadata category.
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
- 2. Call `freshness_report` to layer last-touched timestamps onto each candidate. Stale + unreferenced is stronger evidence than unreferenced alone.
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 the Mermaid bar-chart / treemap returned by the tool grouped by category.
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` on the method to read the offending source.
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` scoped to the class to pinpoint the SOQL/DML-in-loop **source line** the log proved is hot.
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=<ApexMethod>)` — get the source + branch
49
- annotation. Confirm the line in the trace maps to a real branch (SOQL, DML, deref).
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 a package.xml deployment manifest covering a change set and its required dependencies.
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 NOT currently walk `DEPENDS_ON` edges — it returns the set difference as `package.xml` / `destructiveChanges.xml`. Dependency-closure walking is planned future work; for now the user is responsible for any additional parent metadata not surfaced by the diff.
25
- 4. Inspect the result for ambiguous dependencies (managed-package types, namespace mismatches) and surface them as warnings.
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
- Render a **`flowchart LR`** dependency closure graph. The user's explicit change set is one subgraph; auto-pulled dependencies are another. Edge labels carry the dependency reason (`parent-of`, `layout-for`, `required-permission`).
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
- - **Closure additions** — what was pulled in by dependency traversal, grouped by metadata type.
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** — embedded.
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 — it must be METHOD-level.** Snippets are stored only for Apex methods, so `explain_code` needs `ApexMethod:AccountSvc.calculate(N)` (N = arity), **not** a class qname like `ApexClass:AccountSvc` (that returns "No snippet stored"). If the user names a class, first enumerate its methods with `find_nodes(pattern="ApexMethod:AccountSvc.*")` and either pick the method they meant or explain each in turn. For declarative metadata (fields, objects, Flows) 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.
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/trigger/field qname expecting source — only `ApexMethod:...(N)` has a snippet. On "No snippet stored", resolve to a method qname rather than reporting failure.
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
- - **`reason: 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`.
84
- - **`reason: 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.
85
- - **`reason: embedder_unavailable`** (text mode) — the MiniLM runtime isn't present. Fall back to qname mode if the user can name a similar node.
86
- - **`reason: no_neighbours`** — genuinely no similar nodes. Suggest dropping the `label` filter, or running structural `trace_upstream` / `trace_downstream` instead.
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/DML in loops, unbounded queries) and produce a remediation checklist.
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. As of the edge-based detector these are:
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`** — a SOQL query whose `EXECUTES_SOQL` edge is tagged
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
- - **`dml_in_loop`** — same, for DML statements.
28
- - **`no_bulk`** — a trigger that doesn't iterate `Trigger.new` (body-scan path).
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
- Do **not** promise `UNBOUNDED_QUERY`, `NON_SELECTIVE_QUERY`, `CALLOUT_IN_LOOP`,
31
- `LARGE_HEAP`, or `RECURSIVE_TRIGGER` — the tool does not produce them. (Unbounded-
32
- query was removed: on a real org ~100% of SOQL has no WHERE/LIMIT in the captured
33
- query head, so it was pure noise.)
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` over the requested scope (single class, namespace, or full org). The tool returns risk records keyed on the **method** qname (`ApexMethod:Class.method(n)`) with `risk_type` (`soql_in_loop` / `dml_in_loop` / `no_bulk`) and evidence (the query text or DML target).
44
- 2. Group findings by `risk_type`; 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.
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 the Mermaid heat-map (class-by-rule) from the tool response.
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). When the tool returns its own Mermaid heat-map, embed that and skip this template.
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 -->|SOQL_IN_LOOP: high| Risk1[:::hi]
57
- ClassA -->|UNBOUNDED_QUERY: med| Risk2[:::med]
58
- ClassB -->|DML_IN_LOOP: high| Risk3[:::hi]
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 (`soql_in_loop`, `dml_in_loop`, `no_bulk`).
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-*`) to
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/class" | `sf-explain-code` (needs `ApexMethod:Class.m(n)`) | — |
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 pending change in their working tree (uncommitted or branch-vs-base) before merging.
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. Confirm the diff source: working tree, staged, or a branch range (e.g. `origin/main...HEAD`). If unspecified, default to staged + unstaged against the merge-base of `origin/main`.
24
- 2. Call `impact_from_git_diff` with the resolved diff scope. Expect a list of changed metadata nodes plus their downstream dependents grouped by edge type (`READS_FIELD`, `CALLS_METHOD`, `INVOKES_FLOW`, `RENDERS_COMPONENT`, …).
25
- 3. Call `test_gap_intelligence_from_git_diff` on the same diff scope. Cross-reference impacted nodes against existing Apex test coverage and surface the uncovered set.
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 (`CALLS_METHOD`, `READS_FIELD`, `INVOKES_FLOW`, `RENDERS_COMPONENT`).
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` — it does NOT run the ingest. It returns
25
- `{ executed: false, run_this_command: "sfgraph ingest --org <alias>" }`.
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
- 1. Enumerate Vlocity-CMT node categories present in the graph: `vlocity_cmt__DataRaptor`, `vlocity_cmt__IntegrationProcedure`, `vlocity_cmt__OmniScript`, `vlocity_cmt__VlocityCard`.
22
- 2. Enumerate the OmniStudio-on-Core equivalents: `OmniDataTransform`, `OmniIntegrationProcedure`, `OmniProcess` (OmniScript), `OmniUiCard`.
23
- 3. For each pair, count nodes on both sides and count `CANONICAL_OF` edges that mirror a CMT asset to its OmniStudio twin. Report `cmt_count`, `core_count`, `canonical_pairs`, `unmirrored_cmt`, `unmirrored_core`.
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[vlocity_cmt__DataRaptor:Foo]
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[vlocity_cmt__OmniScript:Bar]:::unmirrored
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` with `qname=CustomObject:<name>` to enumerate Lookup/MasterDetail references coming out of it.
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 lines for each Lookup/MasterDetail edge.
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: Profile/PSet -> Read / Edit / Delete / View All / Modify All.
28
- 4. Identify sharing exposure: OWD setting, sharing rules expanding access, role-hierarchy inheritance toggles.
29
- 5. Flag risks: `without sharing` Apex selectors, missing `stripInaccessible` / `WITH SECURITY_ENFORCED`, guest user access, Experience Cloud profile reach.
30
- 6. Render the Mermaid access matrix returned by the tool.
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 between two points in time and which of those changes plausibly explains a reported failure.
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. Establish the two anchors: the "good" snapshot (pre-deploy, last green build, or named checkpoint) and the "bad" anchor (now, post-deploy, or a specific snapshot id). Ask if neither is obvious.
23
- 2. Call `point_in_time_diff` between the two anchors. Note added / removed / modified nodes and edge churn per layer.
24
- 3. Call `what_broke` with the same window plus the user's failure signal (failing test name, error stack, broken UI route). The tool ranks suspect changes by reachability to the failure signal.
25
- 4. Triage in descending suspicion order. For each suspect, summarise the change (what attribute moved) and the dependency path to the failure surface.
26
- 5. Render the Mermaid path-to-failure diagram returned by `what_broke`.
27
- 6. Recommend the smallest revert or follow-up investigation (e.g. "inspect `AccountTrigger.handleInsert` — its SOQL selector lost a filter between snapshot X and Y").
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
- Render a **`flowchart LR`** path-to-failure diagram. Each suspect change is a node on the left; intermediate dependency hops are middle nodes; the failure surface (test, route, error) is the rightmost node. Highlight the top-suspect path with a thicker edge.
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
- S1[ApexClass:AccountTrigger]:::suspect --> H1[Service:AccountSvc] --> F1[Test:AccountTriggerTest]:::fail
36
- S2[Flow:AccountAfterUpdate] --> H1
37
- classDef suspect fill:#fee,stroke:#c00
38
- classDef fail fill:#fcc,stroke:#900,stroke-width:2px
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** — from `<snapshot/anchor>` to `<snapshot/anchor>`.
54
- - **Top suspects** — ordered list with confidence, change summary, and the edge path linking them to the failure.
55
- - **Other changes in window** — collapsed bullet list, not the focus.
56
- - **Mermaid path-to-failure** — embedded from tool output.
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 propose a code fix without first inspecting the suspect change in detail via `sf-cross-layer-trace`.
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 bury the lead — top suspect goes first, not last.
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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=cursor-format.test.d.ts.map
@@ -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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=installer.test.d.ts.map
@@ -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"}