@anysiteio/agent-skills 1.3.0 → 2.0.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 (37) hide show
  1. package/.claude-plugin/marketplace.json +61 -1
  2. package/.claude-plugin/plugin.json +5 -1
  3. package/README.md +27 -33
  4. package/bin/install.js +186 -135
  5. package/bundles.json +19 -0
  6. package/package.json +3 -2
  7. package/skills/anysite-audience-analysis/SKILL.md +110 -38
  8. package/skills/anysite-audience-analysis/references/PLATFORM_COVERAGE.md +38 -19
  9. package/skills/anysite-audience-analysis/references/TOOL_MAPPING.md +16 -11
  10. package/skills/anysite-brand-reputation/SKILL.md +105 -43
  11. package/skills/anysite-competitor-analyzer/SKILL.md +170 -111
  12. package/skills/anysite-competitor-intelligence/SKILL.md +201 -116
  13. package/skills/anysite-competitor-intelligence/references/ANALYSIS_PATTERNS.md +17 -13
  14. package/skills/anysite-content-analytics/SKILL.md +101 -33
  15. package/skills/anysite-crm-account-brief/SKILL.md +71 -0
  16. package/skills/anysite-crm-audit/SKILL.md +59 -0
  17. package/skills/anysite-crm-champions/SKILL.md +76 -0
  18. package/skills/anysite-crm-competitor-intel/SKILL.md +74 -0
  19. package/skills/anysite-crm-enrich/SKILL.md +84 -0
  20. package/skills/anysite-crm-lookalikes/SKILL.md +64 -0
  21. package/skills/anysite-crm-prospect/SKILL.md +90 -0
  22. package/skills/anysite-crm-score/SKILL.md +72 -0
  23. package/skills/anysite-crm-setup/SKILL.md +147 -0
  24. package/skills/anysite-crm-signals/SKILL.md +96 -0
  25. package/skills/anysite-influencer-discovery/SKILL.md +123 -71
  26. package/skills/anysite-lead-generation/SKILL.md +288 -256
  27. package/skills/anysite-lead-generation/references/LINKEDIN_STRATEGIES.md +79 -71
  28. package/skills/anysite-lead-generation/references/WEB_SCRAPING.md +121 -87
  29. package/skills/anysite-market-research/SKILL.md +117 -68
  30. package/skills/anysite-mcp/SKILL.md +128 -0
  31. package/skills/anysite-mcp-migration/SKILL.md +290 -0
  32. package/skills/anysite-person-analyzer/SKILL.md +86 -53
  33. package/skills/anysite-trend-analysis/SKILL.md +119 -55
  34. package/skills/anysite-vc-analyst/SKILL.md +12 -2
  35. package/skills/competitor-discovery/SKILL.md +350 -0
  36. package/skills/customer-pain-mining/SKILL.md +318 -0
  37. package/skills/positioning-map/SKILL.md +314 -0
@@ -9,23 +9,25 @@ Frameworks and templates for analyzing competitors using anysite MCP data.
9
9
  **Strengths**:
10
10
  ```
11
11
  LinkedIn:
12
- - get_linkedin_company → Company size, growth
13
- - get_linkedin_company_posts → Messaging, brand strength
14
- - search_linkedin_users → Leadership quality
12
+ - execute("linkedin", "company", "company", {"company": "..."}) → Company size, growth
13
+ - execute("linkedin", "company", "company_posts", {"urn": ..., "count": 20}) → Messaging, brand strength
14
+ - execute("linkedin", "search", "search_users", {"company_keywords": "...", "title": "...", "count": 50}) → Leadership quality
15
15
 
16
16
  Y Combinator:
17
- - get_yc_company → Funding, pedigree
17
+ - execute("yc", "company", "get", {"slug": "..."}) → Funding, pedigree
18
18
 
19
19
  Social:
20
20
  - Follower counts, engagement rates
21
21
  - Content quality and frequency
22
+ - Use query_cache(cache_key, aggregate=[{"field": "like_count", "function": "avg"}]) for engagement benchmarks
22
23
  ```
23
24
 
24
25
  **Weaknesses**:
25
26
  ```
26
27
  Reddit:
27
- - search_reddit_posts(competitor) → Customer complaints
28
+ - execute("reddit", "search", "search_posts", {"query": "competitor", "count": 50}) → Customer complaints
28
29
  - Product gaps, feature requests
30
+ - Use query_cache(cache_key, sort_by=[{"field": "comment_count", "order": "desc"}]) to find most-discussed issues
29
31
 
30
32
  LinkedIn:
31
33
  - Employee turnover (departures)
@@ -48,8 +50,8 @@ Market gaps identified through:
48
50
  **Threats**:
49
51
  ```
50
52
  Competitive threats:
51
- - YC batch analysis (new entrants)
52
- - Hiring velocity (aggressive growth)
53
+ - YC batch analysis (new entrants): execute("yc", "search", "search_companies", {"query": "...", "count": 50})
54
+ - Hiring velocity (aggressive growth): execute("linkedin", "company", "company_employee_stats", {"urn": ...})
53
55
  - New product launches
54
56
  - Pricing changes
55
57
  ```
@@ -63,16 +65,18 @@ Competitive threats:
63
65
  4. Customer Type (Technical vs. Business Users)
64
66
 
65
67
  **Data Sources**:
66
- - Website messaging
67
- - LinkedIn company description
68
- - Pricing pages
69
- - Job postings (customer success vs. enterprise support)
68
+ - Website messaging: `execute("webparser", "parse", "parse", {"url": "..."})`
69
+ - LinkedIn company description: `execute("linkedin", "company", "company", {"company": "..."})`
70
+ - Pricing pages: `execute("webparser", "parse", "parse", {"url": "<website>/pricing"})`
71
+ - Job postings: `execute("linkedin", "search", "search_jobs", {"keywords": "...", "count": 50})`
72
+
73
+ **Export**: Use `export_data(cache_key, "csv")` to build comparison spreadsheets.
70
74
 
71
75
  ## Porter's Five Forces Analysis
72
76
 
73
77
  **Threat of New Entrants**:
74
- - Monitor YC batches for new competitors
75
- - Track LinkedIn company formations in space
78
+ - Monitor YC batches for new competitors: `execute("yc", "search", "search_companies", {"query": "...", "count": 50})`
79
+ - Track LinkedIn company formations in space: `execute("linkedin", "search", "search_companies", {"keywords": "...", "count": 50})`
76
80
  - Watch for well-funded startups
77
81
 
78
82
  **Competitive Rivalry**:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: anysite-content-analytics
3
- description: Track and analyze content performance across Instagram, YouTube, LinkedIn, Twitter/X, and Reddit using anysite MCP server. Measure engagement metrics, analyze post effectiveness, benchmark content strategy, identify top-performing content, and optimize posting strategies. Supports post performance tracking, engagement analysis, content type comparison, and competitive benchmarking. Use when users need to measure content ROI, optimize social strategy, identify viral content patterns, or analyze content engagement across platforms.
3
+ description: Track and analyze content performance across Instagram, YouTube, LinkedIn, Twitter/X, and Reddit using anysite MCP server. Measure engagement metrics, analyze post effectiveness, benchmark content strategy, identify top-performing content, and optimize posting strategies. Use when users need to measure content ROI, optimize social strategy, identify viral content patterns, or analyze content engagement across platforms.
4
4
  ---
5
5
 
6
6
  # anysite Content Analytics
@@ -25,18 +25,39 @@ Measure and optimize content performance across social platforms using anysite M
25
25
  - ✅ **Twitter/X**: Tweets, retweets, likes, replies
26
26
  - ✅ **Reddit**: Posts, upvotes, comments, awards
27
27
 
28
+ ## v2 Tool Interface
29
+
30
+ All data fetching uses the anysite MCP v2 universal meta-tools:
31
+
32
+ - **`execute(source, category, endpoint, params)`** - Fetch data from any source. Returns first page + `cache_key`.
33
+ - **`get_page(cache_key, offset, limit)`** - Load more items from a previous execute() when `next_offset` is returned.
34
+ - **`query_cache(cache_key, conditions?, sort_by?, aggregate?, group_by?)`** - Filter, sort, and aggregate cached data without new API calls.
35
+ - **`export_data(cache_key, format)`** - Export full dataset as CSV, JSON, or JSONL. Returns a download URL.
36
+
37
+ ### Error Handling
38
+
39
+ v2 responses may include `llm_hint` fields with guidance on how to resolve errors. Common patterns:
40
+ - **412**: Entity not found - verify the identifier (username, URN, URL).
41
+ - **422**: Invalid parameter format - check URN prefix format or param types.
42
+ - Always check `llm_hint` in error responses for specific resolution steps.
43
+
28
44
  ## Quick Start
29
45
 
30
46
  **Step 1: Collect Content Data**
31
47
 
32
48
  Platform-specific:
33
- - Instagram: `get_instagram_user_posts(username, count=50)`
34
- - LinkedIn: `get_linkedin_user_posts(urn, count=50)`
35
- - Twitter: `get_twitter_user_posts(user, count=100)`
36
- - YouTube: `get_youtube_channel_videos(channel, count=30)`
49
+ - Instagram: `execute("instagram", "user", "user_posts", {"user": "username", "count": 50})`
50
+ - LinkedIn: `execute("linkedin", "user", "user_posts", {"urn": "fsd_profile:ACoAAA...", "count": 50})`
51
+ - Twitter: `execute("twitter", "user", "user_posts", {"user": "username", "count": 100})`
52
+ - YouTube: `execute("youtube", "channel", "channel_videos", {"channel": "channel_id", "count": 30})`
37
53
 
38
54
  **Step 2: Analyze Engagement**
39
55
 
56
+ Use `query_cache()` on the returned `cache_key` to analyze without re-fetching:
57
+ ```
58
+ query_cache(cache_key, sort_by="likes desc", aggregate="avg:likes,comments")
59
+ ```
60
+
40
61
  Calculate metrics:
41
62
  - Engagement rate: (likes + comments + shares) / followers
42
63
  - Best performing content: Top 10% by engagement
@@ -59,6 +80,14 @@ Based on findings:
59
80
  - Replicate successful topics
60
81
  - Adjust content mix
61
82
 
83
+ **Step 5: Export Results**
84
+
85
+ ```
86
+ export_data(cache_key, "csv")
87
+ ```
88
+
89
+ Returns a download URL for the full dataset.
90
+
62
91
  ## Common Workflows
63
92
 
64
93
  ### Workflow 1: Instagram Content Audit
@@ -67,7 +96,13 @@ Based on findings:
67
96
 
68
97
  1. **Get All Posts**
69
98
  ```
70
- get_instagram_user_posts(username, count=100)
99
+ execute("instagram", "user", "user_posts", {"user": "username", "count": 100})
100
+ → returns cache_key + first page of results
101
+ ```
102
+
103
+ If more posts exist (response includes `next_offset`):
104
+ ```
105
+ get_page(cache_key, offset=next_offset, limit=50)
71
106
  ```
72
107
 
73
108
  2. **Calculate Metrics**
@@ -78,9 +113,15 @@ For each post:
78
113
  - Content type (Reel, carousel, single image, video)
79
114
  ```
80
115
 
116
+ Use `query_cache` to sort and filter:
117
+ ```
118
+ query_cache(cache_key, sort_by="likes desc", aggregate="avg:likes,comments")
119
+ ```
120
+
81
121
  3. **Identify Top Performers**
82
122
  ```
83
- Sort by engagement rate
123
+ query_cache(cache_key, sort_by="likes desc")
124
+
84
125
  Top 10%: Analyze for common patterns
85
126
  - Topics/themes
86
127
  - Visual style
@@ -90,7 +131,9 @@ Top 10%: Analyze for common patterns
90
131
 
91
132
  4. **Analyze Content Mix**
92
133
  ```
93
- Count by type:
134
+ query_cache(cache_key, group_by="type", aggregate="count:id,avg:likes,avg:comments")
135
+
136
+ Results show:
94
137
  - Reels: X% of posts, Y% of engagement
95
138
  - Carousels: X% of posts, Y% of engagement
96
139
  - Single images: X% of posts, Y% of engagement
@@ -99,7 +142,7 @@ Count by type:
99
142
  5. **Benchmark Against Competitors**
100
143
  ```
101
144
  For each competitor:
102
- get_instagram_user_posts(competitor, count=50)
145
+ execute("instagram", "user", "user_posts", {"user": "competitor", "count": 50})
103
146
  Compare:
104
147
  - Posting frequency
105
148
  - Engagement rates
@@ -107,6 +150,11 @@ Compare:
107
150
  - Top themes
108
151
  ```
109
152
 
153
+ 6. **Export Results**
154
+ ```
155
+ export_data(cache_key, "csv")
156
+ ```
157
+
110
158
  **Expected Output**:
111
159
  - Content performance report
112
160
  - Top 10 performing posts
@@ -120,9 +168,17 @@ Compare:
120
168
 
121
169
  1. **Collect Post History**
122
170
  ```
123
- get_linkedin_user_posts(urn, count=100)
171
+ execute("linkedin", "user", "user_posts", {"urn": "fsd_profile:ACoAAA...", "count": 100})
172
+ → returns cache_key + first page
124
173
  ```
125
174
 
175
+ For company page posts:
176
+ ```
177
+ execute("linkedin", "company", "company_posts", {"urn": {"type": "company", "value": "1441"}, "count": 100})
178
+ ```
179
+
180
+ Use `get_page(cache_key, offset, limit)` if more posts exist.
181
+
126
182
  2. **Categorize Content**
127
183
  ```
128
184
  Group by type:
@@ -136,6 +192,8 @@ Group by type:
136
192
 
137
193
  3. **Analyze Engagement by Type**
138
194
  ```
195
+ query_cache(cache_key, aggregate="avg:comment_count,avg:share_count", group_by="type")
196
+
139
197
  For each content type:
140
198
  - Average reactions
141
199
  - Average comments
@@ -173,13 +231,16 @@ Calculate average engagement for each group
173
231
 
174
232
  1. **Get Channel Videos**
175
233
  ```
176
- get_youtube_channel_videos(channel, count=50)
234
+ execute("youtube", "channel", "channel_videos", {"channel": "channel_id", "count": 50})
235
+ → returns cache_key + first page
177
236
  ```
178
237
 
238
+ Use `get_page(cache_key, offset, limit)` for additional videos.
239
+
179
240
  2. **Analyze Each Video**
180
241
  ```
181
242
  For each video:
182
- get_youtube_video(video_id)
243
+ execute("youtube", "video", "video", {"video": "video_id"})
183
244
 
184
245
  Metrics:
185
246
  - Views
@@ -190,6 +251,8 @@ Metrics:
190
251
 
191
252
  3. **Identify Patterns**
192
253
  ```
254
+ query_cache(cache_key, sort_by="views desc")
255
+
193
256
  Analyze top 20% by views:
194
257
  - Video length
195
258
  - Titles (keywords, style)
@@ -201,7 +264,7 @@ Analyze top 20% by views:
201
264
  4. **Engagement Analysis**
202
265
  ```
203
266
  Check comments:
204
- get_youtube_video_comments(video_id, count=100)
267
+ execute("youtube", "video", "video_comments", {"video": "video_id", "count": 100})
205
268
 
206
269
  Analyze:
207
270
  - Comment quality
@@ -225,44 +288,49 @@ Compare:
225
288
  - Title and thumbnail insights
226
289
  - Upload strategy recommendations
227
290
 
228
- ## MCP Tools Reference
291
+ ## MCP Tools Reference (v2)
229
292
 
230
293
  ### Instagram
231
- - `get_instagram_user_posts(user, count)` - Get posts with engagement
232
- - `get_instagram_post(post_id)` - Get detailed post metrics
233
- - `get_instagram_post_likes(post, count)` - Analyze likers
234
- - `get_instagram_post_comments(post, count)` - Get comments
294
+ - `execute("instagram", "user", "user_posts", {"user": username, "count": N})` - Get posts with engagement
295
+ - `execute("instagram", "post", "post", {"post": post_id})` - Get detailed post metrics
296
+ - `execute("instagram", "post", "post_likes", {"post": post_id, "count": N})` - Analyze likers
297
+ - `execute("instagram", "post", "post_comments", {"post": post_id, "count": N})` - Get comments
235
298
 
236
299
  ### LinkedIn
237
- - `get_linkedin_user_posts(urn, count)` - Get post history
238
- - `get_linkedin_company_posts(urn, count)` - Company page posts
300
+ - `execute("linkedin", "user", "user_posts", {"urn": "fsd_profile:ACoAAA...", "count": N})` - Get user post history
301
+ - `execute("linkedin", "company", "company_posts", {"urn": {"type": "company", "value": "ID"}, "count": N})` - Company page posts
239
302
 
240
303
  ### Twitter/X
241
- - `get_twitter_user_posts(user, count)` - Get tweets
242
- - `search_twitter_posts(query, count)` - Find trending tweets
304
+ - `execute("twitter", "user", "user_posts", {"user": username, "count": N})` - Get tweets
305
+ - `execute("twitter", "search", "search_posts", {"query": query, "count": N})` - Find trending tweets
243
306
 
244
307
  ### YouTube
245
- - `get_youtube_channel_videos(channel, count)` - All videos
246
- - `get_youtube_video(video)` - Video details and metrics
247
- - `get_youtube_video_comments(video, count)` - Comments
308
+ - `execute("youtube", "channel", "channel_videos", {"channel": channel, "count": N})` - All videos
309
+ - `execute("youtube", "video", "video", {"video": video_id})` - Video details and metrics
310
+ - `execute("youtube", "video", "video_comments", {"video": video_id, "count": N})` - Comments
248
311
 
249
312
  ### Reddit
250
- - `reddit_user_posts(username, count)` - User's posts
251
- - `search_reddit_posts(query, count)` - Find popular posts
313
+ - `execute("reddit", "user", "user_posts", {"username": username, "count": N})` - User's posts
314
+ - `execute("reddit", "search", "search_posts", {"query": query, "count": N})` - Find popular posts
315
+
316
+ ### Pagination & Analysis
317
+ - `get_page(cache_key, offset, limit)` - Fetch next page of results from any execute() call
318
+ - `query_cache(cache_key, conditions?, sort_by?, aggregate?, group_by?)` - Filter/sort/aggregate cached results
319
+ - `export_data(cache_key, "csv"|"json"|"jsonl")` - Export dataset as downloadable file
252
320
 
253
321
  ## Key Metrics
254
322
 
255
323
  **Engagement Rate**:
256
- - Formula: (Likes + Comments + Shares) / Followers × 100
324
+ - Formula: (Likes + Comments + Shares) / Followers x 100
257
325
  - Instagram benchmark: 3-6%
258
326
  - LinkedIn benchmark: 2-5% of connections
259
327
  - Twitter benchmark: 0.5-1%
260
328
 
261
329
  **Content Performance Score**:
262
330
  ```
263
- Score = (Engagement Rate × 40) +
264
- (Comments/Likes Ratio × 30) +
265
- (Share Rate × 30)
331
+ Score = (Engagement Rate x 40) +
332
+ (Comments/Likes Ratio x 30) +
333
+ (Share Rate x 30)
266
334
  ```
267
335
 
268
336
  **Viral Potential Indicators**:
@@ -278,13 +346,13 @@ Score = (Engagement Rate × 40) +
278
346
  - Key insights and patterns
279
347
  - Recommendations for optimization
280
348
 
281
- **CSV Export**:
349
+ **CSV Export** (via `export_data(cache_key, "csv")`):
282
350
  - Post URL, date, type
283
351
  - Likes, comments, shares
284
352
  - Engagement rate
285
353
  - Performance rank
286
354
 
287
- **JSON Export**:
355
+ **JSON Export** (via `export_data(cache_key, "json")`):
288
356
  - Full post data with metadata
289
357
  - Time-series engagement data
290
358
  - Historical trends
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: anysite-crm-account-brief
3
+ description: Pre-meeting brief for an account that lives in the connected CRM - what the CRM already knows (fields, contacts, history) merged with fresh outside data (funding, exec changes, news, key people's recent LinkedIn activity) into a one-page brief with talking points. Read-only. Use when the user preps for a call/meeting/demo with a CRM account or asks for account research in the context of their pipeline. Requires an active CRM connection - for briefs on arbitrary companies without CRM context, other research skills apply.
4
+ ---
5
+
6
+ # CRM Account Brief
7
+
8
+ One account, twenty minutes of research, one page the user can read in the elevator.
9
+
10
+ ## Flow
11
+
12
+ ### 1. CRM context (if connected)
13
+
14
+ ```
15
+ crm_query_records(object_type="companies", search="<name or domain>")
16
+ crm_query_records(object_type="contacts", search="<domain>") # linked people
17
+ ```
18
+ What we already know: fields, mapped scores/signals, who we talk to, what stage. The brief
19
+ must not contradict the CRM — where outside data disagrees (e.g. new title), flag it as
20
+ an update candidate for `anysite-crm-enrich`/`anysite-crm-champions`.
21
+
22
+ ### 2. Company snapshot
23
+
24
+ - `execute crunchbase/search {keywords: name}` → alias → `crunchbase/company` →
25
+ funding history, `leadership_hires[]`, `news[]`, `layoffs[]`, employee range, investors.
26
+ - `execute linkedin/search/search_sql_companies {website: domain}` → description,
27
+ specialities, locations, employee_count.
28
+
29
+ ### 3. What's happening now
30
+
31
+ - Hiring: `linkedin/search/search_jobs {company: [{"type": "company", "value": "<id>"}],
32
+ sort: "recent", count: 20}` (numeric id from the `fsd_company:<id>` URN) — what functions
33
+ they're growing (that's their current priorities, use in talking points).
34
+ - News: crunchbase `news[]` first (already fetched); add
35
+ `techmeme/stories/stories_search {keyword: "<name>", count: 5}` for tech companies.
36
+ - Employer sentiment (optional, for bigger companies): resolve the employer id first via
37
+ `glassdoor/companies/companies_search {company: "<name>", count: 1}` → then
38
+ `companies_ratings {company: <id>}`; `blind/companies/companies_reviews` — morale,
39
+ attrition themes. Use with care in messaging — background context, never a quoted opener.
40
+
41
+ ### 4. The people in the room
42
+
43
+ For each known attendee / key CRM contact with linkedin_url:
44
+ ```
45
+ execute linkedin/user/user {user: <url>} → role, tenure, background
46
+ execute linkedin/user/user_posts {urn, count: 10,
47
+ posted_after: <90 days ago>} → what they talk about
48
+ ```
49
+ Caveat: `user` called with a URL may omit the `urn` in its response, and `user_posts`
50
+ accepts ONLY a URN. If the urn is missing, recover it via
51
+ `search_users {first_name, last_name, company_keywords, count: 3}` → pick the match →
52
+ use its urn. Posts are personalization gold: real interests, stated problems, conference
53
+ activity.
54
+ No posts ≠ no signal — check `user_comments` for lurker activity if it matters.
55
+
56
+ ### 5. The brief
57
+
58
+ One page, this order:
59
+ 1. **Snapshot** — what they do, size, stage, funding, trajectory (3 lines).
60
+ 2. **What's new** — dated events (funding, hires, launches, layoffs), newest first.
61
+ 3. **People** — attendees with one-line "who they are + what they care about".
62
+ 4. **Angle** — 3 talking points tied to evidence, 1–2 risks/landmines (layoffs, churned
63
+ history in CRM, competitor relationship).
64
+ 5. **Sources** — links for every claim. No link → don't claim it.
65
+
66
+ ## Writes
67
+
68
+ None by default. If the user asks to save the brief: a note via the CRM UI is their
69
+ fastest path (note-writing is not exposed through crm_* tools); offer the brief as text
70
+ they can paste, or stamp mapped summary fields via `crm_upsert_companies` (dry-run first)
71
+ only if the profile maps them.
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: anysite-crm-audit
3
+ description: Read-only data quality audit of the connected CRM - field completeness per mapped property, duplicate candidates, stale records, enrichability estimate. Produces a report and a concrete fix plan, never writes anything - run it first when you don't yet know what's broken; the actual fixing happens in anysite-crm-enrich. Use when the user asks "what's the state of my CRM", wants a data quality report, duplicate check, or a safe first run after connecting a CRM. Requires an active CRM connection.
4
+ ---
5
+
6
+ # CRM Audit
7
+
8
+ The safe first date with a user's CRM: read everything, write nothing, show what's broken
9
+ and what enrichment would fix. Also the natural funnel into `anysite-crm-enrich`.
10
+
11
+ ## Prerequisites
12
+
13
+ Active CRM connection. No profile needed (works better with one — gap analysis follows the
14
+ mapped fields).
15
+
16
+ ## Flow
17
+
18
+ ### 1. Inventory
19
+
20
+ ```
21
+ crm_get_schema(object_type="contacts") / (object_type="companies")
22
+ ```
23
+ Use `properties` filtering when you only need specific fields — the full schema is large.
24
+ Then page through records:
25
+ ```
26
+ crm_query_records(object_type=..., properties=[key fields], after=<cursor>)
27
+ ```
28
+ Audit scope: whole portal if small; else the working list + a stated sample. Say which.
29
+
30
+ ### 2. Analyze (all local — no further API calls)
31
+
32
+ - **Completeness:** % filled per mapped/target field (email, title, company, domain,
33
+ industry, size, linkedin_url). Rank by gap size.
34
+ - **Match-key coverage:** how many contacts have email / linkedin_url / neither (the
35
+ "neither" group is unenrichable and un-dedupable — flag it).
36
+ - **Duplicate candidates:** same normalized email; same domain with several company records;
37
+ same person name + company. Candidates only — never auto-merge.
38
+ - **Staleness:** if `anysite_last_enriched_at` (or similar) is mapped — age distribution;
39
+ contacts with no activity fields; companies with dead domains (spot-check a few via
40
+ `webparser/parse` only if the user asks).
41
+ - **Association health:** contacts without a company link (visible as missing company
42
+ fields when queried).
43
+
44
+ ### 3. Report
45
+
46
+ Lead with the 3 numbers that matter: enrichable-now count, duplicate-candidate count,
47
+ unenrichable count. Then per-field completeness table, then the fix plan:
48
+
49
+ - "N contacts have linkedin_url but no title → `anysite-crm-enrich` fills them (~cost)"
50
+ - "N companies missing industry/size → enrich via company lookup (~cost)"
51
+ - "N duplicate candidates → list for manual review (merging is a CRM-side operation)"
52
+
53
+ Cost estimates come from the anysite-mcp source map (credits per call × volume). Offer to
54
+ run the enrichment as the next step — do not run it from this skill.
55
+
56
+ ## Hard rule
57
+
58
+ This skill performs zero writes: no upserts, no dry-runs, no "small fixes". Its value is
59
+ that it is provably safe to run on day one.
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: anysite-crm-champions
3
+ description: Detect job changes among CRM contacts (champion tracking) - find contacts who moved to a new company, flag past champions at new accounts, update the CRM and propose re-engagement plays. Widely considered the highest-converting B2B signal. Use specifically for job-change detection - "who changed jobs", "track champions", "are my contacts still there". For general field updates on contacts use anysite-crm-enrich instead. Requires an active CRM connection and contacts with linkedin_url or email.
4
+ ---
5
+
6
+ # CRM Champion Tracking
7
+
8
+ People move; CRMs rot (typically ~25%/year of contacts change jobs). A past champion at a
9
+ new company is warm pipeline. This skill finds the movers and turns them into plays.
10
+
11
+ ## Prerequisites
12
+
13
+ Active CRM connection. Profile mapping for any writes — the Writing rules in
14
+ `anysite-crm-setup` apply, including its create policy: creating records here follows the
15
+ profile's `allow_create`, same as in `anysite-crm-prospect`. Contacts need `linkedin_url`
16
+ or `email` — run `anysite-crm-enrich` first if coverage is poor.
17
+
18
+ ## Flow
19
+
20
+ ### 1. Pick who to track
21
+
22
+ Priority order (ask the user which tier, default to the first available):
23
+ 1. **Key champions** — closed-won contacts, power users, admins (if such a list/field exists),
24
+ 2. **Open/closed-lost opportunity contacts**,
25
+ 3. The working list / everyone with a linkedin_url.
26
+
27
+ ```
28
+ crm_query_records(object_type="contacts", list_id=... | search=...,
29
+ properties=[record_id, email, linkedin_url, <company field>, jobtitle])
30
+ ```
31
+
32
+ Cap a run at ~100 contacts (one profile call each); more → propose batching by tier.
33
+
34
+ ### 2. Detect moves
35
+
36
+ Per contact:
37
+ - `linkedin_url` → `execute linkedin/user/user {user: <url>}` → current experience.
38
+ - email only → `execute linkedin/email/email_sql_user {email}` → profile (live `email_user`
39
+ for the remainder).
40
+
41
+ Compare the profile's **current company** against the CRM company. Normalize before
42
+ comparing (legal suffixes, casing, known rebrands); when unsure, treat as "same" — false
43
+ move-alarms erode trust. Also catch **promotions** (same company, new title) — a secondary
44
+ but useful signal.
45
+
46
+ ### 3. Classify and propose plays
47
+
48
+ - **Moved to an in-ICP company** → hottest: "past champion at new account" play. Propose:
49
+ update old record, create the new-company record and a fresh contact entry.
50
+ - **Moved out of ICP** → update CRM only.
51
+ - **Promoted** → update title; suggest congratulation touch if they're an active deal contact.
52
+ - **Profile gone/private** → report, no change.
53
+
54
+ ### 4. Write back (with explicit user confirmation)
55
+
56
+ Job-change writes touch the fields most likely to collide with CRM automations, so always
57
+ dry-run and show the diff, even for small batches:
58
+
59
+ - Update the old contact: new title/company per profile mapping (these need `overwrite` in
60
+ the profile — job data is volatile by design).
61
+ - New account: `crm_upsert_companies` (match by domain, `allow_create` per profile).
62
+ - New email at the new company: try `user_email`; the higher-yield `find_email_by_url` may
63
+ be disabled in MCP — check `discover("linkedin", "user")` before promising it. Note that
64
+ creating a NEW contact record requires an email; without one, update the existing record.
65
+ - Association: pass `associate_company_domain` (or `associate_company_id` from the company
66
+ upsert result) so the contact links to the new company; the server keeps the old
67
+ association unless `overwrite_associations=true` — set it only if the user confirms the
68
+ contact should be re-linked.
69
+
70
+ Save `run_id`s; mention `crm_undo`.
71
+
72
+ ### 5. Report
73
+
74
+ Table: contact → old → new → play. Lead with movers into ICP accounts. Include suggested
75
+ opener anchored on the shared history ("you used X at <old company>...") — personalization
76
+ from facts, never invented familiarity.
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: anysite-crm-competitor-intel
3
+ description: Displacement hunting tied to the CRM - find companies using a competitor's product (Wappalyzer technographics), mine switching reasons and pains from software reviews (Capterra switched_from/switching_reason, TrustRadius, GetApp), cross-reference with CRM accounts and tag displacement targets. Use ONLY when the goal is a CRM-tied displacement list or competitor-user tagging. For general competitor strategy research (content, hiring, positioning, no CRM) use anysite-competitor-intelligence.
4
+ ---
5
+
6
+ # CRM Competitor Intel
7
+
8
+ Competitor-switch signals are the highest-intent plays in signal-based selling. This skill
9
+ builds the target list and the ammunition: who uses the competitor, and what their users
10
+ complain about.
11
+
12
+ ## Prerequisites
13
+
14
+ Active CRM connection for the cross-reference/tagging part (Writing rules from
15
+ `anysite-crm-setup` apply to tagging). The research part works without one.
16
+
17
+ ## Flow
18
+
19
+ ### 1. Who uses the competitor (technographics)
20
+
21
+ Only works for competitors whose product is detectable on websites (martech, analytics,
22
+ chat widgets, ecommerce...):
23
+ ```
24
+ execute wappalyzer/technologies {technology: "<competitor slug>"}
25
+ → website_count (market size), top_websites[] (sample of users, with traffic/tech-spend),
26
+ alternatives[] (the category landscape), top_countries
27
+ ```
28
+ Honest limitation: `top_websites` is a **sample**, not an exhaustive list. Frame it as
29
+ "examples + market sizing", supplement with `linkedin/search/search_sql_companies`
30
+ searching the competitor name in `specialities`/`description`, and with
31
+ `producthunt/products/products_alternatives` for the category graph.
32
+
33
+ Not website-detectable (e.g. a database vendor)? Skip to reviews and search: job posts
34
+ mentioning the tool (`linkedin/search/search_jobs {keywords: "<tool>"}` — companies whose
35
+ vacancies require competitor experience are its customers), reddit/community mentions.
36
+
37
+ ### 2. What their users complain about (review mining)
38
+
39
+ ```
40
+ execute capterra/products/products_search {query: "<competitor>", count: 5} → seo_id
41
+ execute capterra/products/products_reviews {product: "<seo_id>", count: 50}
42
+ ```
43
+ The `product` param wants the `seo_id` field (not `id`). Reviews include `switched_from[]`,
44
+ `switching_reason` and `chosen_reason` — direct competitor-switch evidence, mine these
45
+ first. Same pattern via `trustradius` and `getapp` `products_reviews` (g2 exposes search
46
+ only). Filter low-rating reviews with `query_cache` (free), then extract with the LLM:
47
+ recurring pains, switching triggers, praised alternatives, verbatim quotes worth reusing.
48
+ Keep 3–7 pains with quote + source URL each — this is the personalization ammunition.
49
+
50
+ ### 3. Cross-reference with the CRM
51
+
52
+ ```
53
+ crm_query_records(object_type="companies", search=<domain from step 1>)
54
+ ```
55
+ Split: already in CRM (mark as competitor-user) / net-new fits (candidates for
56
+ `anysite-crm-prospect`).
57
+
58
+ ### 4. Tag and hand off
59
+
60
+ If the profile maps a field like `competitor_tool` / `displacement_target`:
61
+ ```
62
+ crm_upsert_companies(records=[{domain: "<domain>", properties:{...}}], allow_create=false,
63
+ dry_run=true) → confirm → write
64
+ ```
65
+ (Company upserts match only by domain.)
66
+ Report: market sizing, tagged accounts, pain library with quotes, and suggested play
67
+ ("lead with <pain #1>, they're on <competitor> per <evidence>"). Evidence links always —
68
+ a displacement claim without a source is a guess, label it as such.
69
+
70
+ ## Boundaries
71
+
72
+ - CRM-tied targeting lives here; broad competitor strategy analysis (content, hiring,
73
+ positioning) → `anysite-competitor-intelligence` skill.
74
+ - Net-new companies go through `anysite-crm-prospect` (dedup + create rules), not directly.