@vruum/skills 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +8 -7
- package/agents/vruum-deal-reviewer.md +5 -8
- package/agents/vruum-engagement-reviewer.md +17 -15
- package/agents/vruum-outreach-reviewer.md +11 -13
- package/package.json +2 -2
- package/skills/campaign-builder/SKILL.md +63 -0
- package/skills/campaign-doctor/SKILL.md +13 -13
- package/skills/create-content/SKILL.md +15 -15
- package/skills/csv-pipeline-fill/SKILL.md +3 -3
- package/skills/deal-triage/SKILL.md +14 -14
- package/skills/engagement-triage/SKILL.md +15 -13
- package/skills/engagement-triage/references/content-subagent-instructions.md +1 -1
- package/skills/engagement-triage/references/engagement-subagent-instructions.md +13 -11
- package/skills/enrich-prospect/SKILL.md +5 -5
- package/skills/expansion-fill/COHORT-QUERIES.md +18 -17
- package/skills/expansion-fill/SKILL.md +20 -17
- package/skills/outreach-triage/SKILL.md +10 -10
- package/skills/outreach-triage/references/subagent-instructions.md +5 -5
- package/skills/pipeline-fill/RESEARCH-ENGINE.md +36 -33
- package/skills/pipeline-fill/SKILL.md +10 -10
- package/skills/sales-nav-deep-fill/SKILL.md +4 -4
- package/skills/winback-fill/COHORT-QUERIES.md +9 -9
- package/skills/winback-fill/SKILL.md +19 -16
- package/skills/yc-pipeline-fill/SKILL.md +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vruum",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Vruum AI skills + remote MCP server for B2B GTM teams. Slash commands for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis, paired with the full Vruum MCP tool surface over OAuth 2.1.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vruum AI",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vruum",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Vruum AI skills + remote MCP server for B2B GTM teams. Skills for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis, paired with the full Vruum MCP tool surface over OAuth 2.1.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vruum AI",
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Vruum AI skills for Claude Code, Codex CLI, and any AI assistant with a skill directory.
|
|
4
4
|
|
|
5
|
-
Pairs with the Vruum MCP server at [https://api.vruum.ai/mcp](https://api.vruum.ai/mcp). The MCP server exposes the `
|
|
5
|
+
Pairs with the Vruum MCP server at [https://api.vruum.ai/mcp](https://api.vruum.ai/mcp). The MCP server exposes the `skill` tool (action=invoke to run a skill, action=publish to publish one), with `search` type=skills and `fetch` type=skill for discovery, so any connected client can run these skills. Skills themselves are distributed via the Claude Code plugin / marketplace; this npm package installs them into the agent-standard skills directory for harnesses without plugin support (Codex CLI, ChatGPT, Windsurf).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -18,7 +18,7 @@ Add to `~/.claude.json`:
|
|
|
18
18
|
}
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Install the skills via the Claude Code plugin / marketplace (below); the MCP server provides the `
|
|
21
|
+
Install the skills via the Claude Code plugin / marketplace (below); the MCP server provides the `skill` tool (action=invoke). You don't need this npm package for Claude Code.
|
|
22
22
|
|
|
23
23
|
### Codex CLI
|
|
24
24
|
|
|
@@ -45,7 +45,7 @@ npx @vruum/skills install --target /path/to/skills/dir # any other harness
|
|
|
45
45
|
|
|
46
46
|
### Cursor / VS Code Copilot / Cline
|
|
47
47
|
|
|
48
|
-
Register the MCP server via your editor's MCP setup (each one has its own UI / config path). Connect to `https://api.vruum.ai/mcp` (HTTP, OAuth via standard MCP flow). The `
|
|
48
|
+
Register the MCP server via your editor's MCP setup (each one has its own UI / config path). Connect to `https://api.vruum.ai/mcp` (HTTP, OAuth via standard MCP flow). The `skill` tool (action=invoke) becomes available; to also install the skill files locally, use this npm package's `install --target` below.
|
|
49
49
|
|
|
50
50
|
### Claude Desktop / Claude.ai (Cowork)
|
|
51
51
|
|
|
@@ -64,6 +64,10 @@ npx @vruum/skills install --target /path/to/skills/dir
|
|
|
64
64
|
## Skills
|
|
65
65
|
|
|
66
66
|
<!-- generated:skills-begin -->
|
|
67
|
+
- `/campaign-builder` — Build and launch an outreach campaign from criteria in about five prompts: filter contacts by size, industry, persona, region, or list; preview the cohort; create the campaign (optionally cloning messaging from an existing one); assign people; review and launch. Use when: create a campaign, build a campaign, new campaign from criteria, campaign from my list.
|
|
68
|
+
- `/campaign-doctor` — Diagnose and fix struggling outreach campaigns. Use when: fix a campaign, diagnose campaign, why is my campaign not working, campaign health, low reply rate, check campaigns, which campaigns need help.
|
|
69
|
+
- `/create-content` — Co-produce an on-voice LinkedIn content post — pull your own signal, steer the angle conversationally, draft in your voice, then save as draft, schedule, or publish. Use when: write a post, draft LinkedIn content, create content, post about, content co-production, help me write a post.
|
|
70
|
+
- `/csv-pipeline-fill` — CSV harness source for /pipeline-fill. Reads a CSV, auto-detects headers, maps columns, hands off to /pipeline-fill for harness deep research and import. Use when: import CSV, paste a CSV, csv import, prospect list from CSV, csv harness mode.
|
|
67
71
|
- `/deal-triage` — Triage your active deal pipeline. Flags at-risk deals, surfaces stalled-deal alerts, runs MEDDIC qualification, and recommends next actions. Use when: review deals, triage deals, check pipeline, deal review, morning deals, pipeline review, deal health, at-risk deals.
|
|
68
72
|
- `/diagnose-reply` — Diagnose why a reply happened — what worked or didn't in the outreach that triggered it. Use when: why did they reply, what worked, diagnose reply, reply diagnosis, analyze this reply, what caused this reply, reply analysis.
|
|
69
73
|
- `/engagement-triage` — Review and approve your pending LinkedIn engagement drafts and demand-gen content posts. Use when: triage engagements, review engagement queue, review warming comments, review nurture reactions, review marketing comments, review content drafts, check engagement queue.
|
|
@@ -72,12 +76,9 @@ npx @vruum/skills install --target /path/to/skills/dir
|
|
|
72
76
|
- `/outreach-triage` — Review and approve your pending outreach drafts across LinkedIn and email. Use when: triage, review queue, morning review, check messages, approve outreach, what needs review.
|
|
73
77
|
- `/pipeline-fill` — Source-agnostic pipeline orchestrator. Picks a source per segment (Sales Nav / YC / CSV / discovery), runs harness deep research, applies a pre-filter gate, then saves into the segment via the backend authoritative match_score>=70 gate. Use when: fill pipeline, import prospects, daily imports, need more prospects, discover prospects from scratch, deep research before import.
|
|
74
78
|
- `/sales-nav-deep-fill` — Sales Nav harness source for /pipeline-fill. Pre-filters Sales Nav profiles via vruum-pipeline-filter, produces a candidate list, hands off to /pipeline-fill for deep research and import. Use when: sales nav with deep research, sales nav harness mode, in-chat sales nav.
|
|
75
|
-
- `/yc-pipeline-fill` — YC harness source for /pipeline-fill. Scrapes YC's public Algolia index, extracts founder LinkedIn URLs, dedups, hands a candidate list to /pipeline-fill for deep research and import. Use when: YC pipeline fill, source from YC, fill segment with YC founders, sales nav dried up, source YC.
|
|
76
|
-
- `/csv-pipeline-fill` — CSV harness source for /pipeline-fill. Reads a CSV, auto-detects headers, maps columns, hands off to /pipeline-fill for harness deep research and import. Use when: import CSV, paste a CSV, csv import, prospect list from CSV, csv harness mode.
|
|
77
|
-
- `/create-content` — Co-produce an on-voice LinkedIn content post — pull your own signal, steer the angle conversationally, draft in your voice, then save as draft, schedule, or publish. Use when: write a post, draft LinkedIn content, create content, post about, content co-production, help me write a post.
|
|
78
|
-
- `/campaign-doctor` — Diagnose and fix struggling outreach campaigns. Use when: fix a campaign, diagnose campaign, why is my campaign not working, campaign health, low reply rate, check campaigns, which campaigns need help.
|
|
79
79
|
- `/vruum-skills-upgrade` — Upgrade @vruum/skills to the latest npm version and re-sync ~/.vruum/. Use when: upgrade vruum skills, update vruum, pull latest vruum skills, or when the preamble reports UPGRADE_AVAILABLE.
|
|
80
80
|
- `/winback-fill` — Source winback candidates from closed-lost deals or churned customers. Surfaces people who went silent or lost a deal >90 days ago, where the loss reason wasn't 'no_fit'. Use when: winback, win back churned, reactivate, revive cold deals, 90-day silent revival, lost deal recovery, lost customer outreach.
|
|
81
|
+
- `/yc-pipeline-fill` — YC harness source for /pipeline-fill. Scrapes YC's public Algolia index, extracts founder LinkedIn URLs, dedups, hands a candidate list to /pipeline-fill for deep research and import. Use when: YC pipeline fill, source from YC, fill segment with YC founders, sales nav dried up, source YC.
|
|
81
82
|
<!-- generated:skills-end -->
|
|
82
83
|
|
|
83
84
|
## Upgrade
|
|
@@ -4,14 +4,11 @@ description: Read-only deal reviewer. Analyzes a single deal — risk, qualifica
|
|
|
4
4
|
mcpServers:
|
|
5
5
|
- vruum
|
|
6
6
|
tools:
|
|
7
|
-
-
|
|
8
|
-
-
|
|
7
|
+
- mcp__vruum__fetch
|
|
8
|
+
- mcp__vruum__search
|
|
9
9
|
- mcp__vruum__get_deal_360
|
|
10
|
-
- mcp__vruum__get_deal_alerts
|
|
11
10
|
- mcp__vruum__inspect_pipeline
|
|
12
11
|
- mcp__vruum__get_person_360
|
|
13
|
-
- mcp__vruum__get_company_research
|
|
14
|
-
- mcp__vruum__get_account_state
|
|
15
12
|
- WebSearch
|
|
16
13
|
- WebFetch
|
|
17
14
|
---
|
|
@@ -26,13 +23,13 @@ You are **read-only by tool surface** — your tool list contains no write tools
|
|
|
26
23
|
|
|
27
24
|
For each deal you're assigned:
|
|
28
25
|
|
|
29
|
-
1. **Get full deal context**: Call `get_deal_360` with the `deal_id`. This returns deal info, stakeholders (read-only view), MEDDIC qualification state (whatever's already on the deal), and recent activity timeline — all in one call. If that endpoint isn't in your tool list, fall back to `
|
|
26
|
+
1. **Get full deal context**: Call `get_deal_360` with the `deal_id`. This returns deal info, stakeholders (read-only view), MEDDIC qualification state (whatever's already on the deal), and recent activity timeline — all in one call. If that endpoint isn't in your tool list, fall back to `fetch` with type=deal (which carries `qualification` / `qualification_score` if previously computed; stakeholder count is in the deal row).
|
|
30
27
|
|
|
31
|
-
2. **Read qualification state — do NOT run qualification.** Inspect `qualification` / `qualification_score` from `get_deal_360`'s response. If `qualification` is null OR `qualification_score < 40` OR the last qualification is older than 30 days, surface this as a `re_qualify` recommendation in your output. Do NOT
|
|
28
|
+
2. **Read qualification state — do NOT run qualification.** Inspect `qualification` / `qualification_score` from `get_deal_360`'s response. If `qualification` is null OR `qualification_score < 40` OR the last qualification is older than 30 days, surface this as a `re_qualify` recommendation in your output. Do NOT run qualification yourself (`manage_deal` action=qualify) — it writes a fresh MEDDIC JSONB on the deal and burns LLM tokens. The orchestrator runs it only after the seller approves.
|
|
32
29
|
|
|
33
30
|
3. **Research primary stakeholder**: Call `get_person_360` for the primary champion (or first stakeholder). Note match score, research highlights, and recent activity.
|
|
34
31
|
|
|
35
|
-
4. **Read account state**: Call `
|
|
32
|
+
4. **Read account state**: Call `fetch` with type=account_state for the deal's account stage + health (lifecycle: prospect → engaged → committed → onboarded → adopting → expansion_ready → dormant → churned). If 404 (no row yet), default to `prospect` / null health.
|
|
36
33
|
|
|
37
34
|
5. **Optional external context**: Use `WebSearch` / `WebFetch` for recent company news that might affect the deal (funding, layoffs, acquisitions). Only when it materially affects the recommendation — don't routinely web-search every deal.
|
|
38
35
|
|
|
@@ -5,9 +5,9 @@ mcpServers:
|
|
|
5
5
|
- vruum
|
|
6
6
|
tools:
|
|
7
7
|
- mcp__vruum__get_engagement_review
|
|
8
|
-
-
|
|
8
|
+
- mcp__vruum__manage_engagements
|
|
9
9
|
- mcp__vruum__get_content_review
|
|
10
|
-
-
|
|
10
|
+
- mcp__vruum__manage_content
|
|
11
11
|
- mcp__vruum__get_person_360
|
|
12
12
|
- WebSearch
|
|
13
13
|
- WebFetch
|
|
@@ -20,7 +20,7 @@ You are an engagement uplift + review agent with access to 5 Vruum MCP tools. Yo
|
|
|
20
20
|
|
|
21
21
|
The orchestrator will tell you which type and provide IDs.
|
|
22
22
|
|
|
23
|
-
If your dispatch prompt includes an instruction block about scoping MCP calls to a specific company, follow those instructions exactly. Before any `
|
|
23
|
+
If your dispatch prompt includes an instruction block about scoping MCP calls to a specific company, follow those instructions exactly. Before any `manage_engagements` write, verify the returned item's `user_company_id` matches the scope the orchestrator gave you (if any).
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
@@ -54,7 +54,7 @@ Skip commenting and recommend a reaction when:
|
|
|
54
54
|
- Commenting would require the sender to pretend they have expertise they don't have
|
|
55
55
|
- The post is personal/religious/emotional and a comment from a stranger feels performative
|
|
56
56
|
|
|
57
|
-
Use `
|
|
57
|
+
Use `manage_engagements` with action="edit" to change the engagement_type to a reaction, or recommend SKIP_TO_REACTION in your summary so the orchestrator can convert it.
|
|
58
58
|
|
|
59
59
|
A real person likes 20 posts for every 1 they comment on. The bar for commenting should be HIGH.
|
|
60
60
|
|
|
@@ -105,20 +105,22 @@ Flag if the reaction type seems wrong for the post context.
|
|
|
105
105
|
### 2h. Budget check
|
|
106
106
|
If `budget_status` shows the sender account is near daily limits, note it in REASONING.
|
|
107
107
|
|
|
108
|
-
## Step 3: Apply the uplift via
|
|
108
|
+
## Step 3: Apply the uplift via manage_engagements
|
|
109
109
|
|
|
110
|
-
When you decide to UPLIFT, write back via `
|
|
110
|
+
When you decide to UPLIFT, write back via `manage_engagements` with **action="edit"** AND **polish_provenance** in the payload so the two-stage edit diff is captured:
|
|
111
111
|
|
|
112
112
|
```
|
|
113
|
-
|
|
114
|
-
engagement_ids="<id>",
|
|
113
|
+
manage_engagements(
|
|
115
114
|
action="edit",
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
id="<id>",
|
|
116
|
+
payload={
|
|
117
|
+
"content": "<your uplifted comment>",
|
|
118
|
+
"polish_provenance": {
|
|
119
|
+
"source": "skill",
|
|
120
|
+
"model": "<your model — claude-opus-4-7, claude-sonnet-4-6, etc.>",
|
|
121
|
+
"at": "<ISO8601 timestamp>",
|
|
122
|
+
"rewrite_notes": "<one line: what changed and why>"
|
|
123
|
+
}
|
|
122
124
|
}
|
|
123
125
|
)
|
|
124
126
|
```
|
|
@@ -198,7 +200,7 @@ If the post makes specific claims (statistics, market sizes), flag as "unverifie
|
|
|
198
200
|
|
|
199
201
|
## Step 3: Edit if needed
|
|
200
202
|
|
|
201
|
-
For structural issues (no line breaks, weak hook), use `
|
|
203
|
+
For structural issues (no line breaks, weak hook), use `manage_content` with action="edit". For brand voice and factual issues, FLAG rather than edit.
|
|
202
204
|
|
|
203
205
|
## Step 4: Return structured summary
|
|
204
206
|
|
|
@@ -5,18 +5,16 @@ mcpServers:
|
|
|
5
5
|
- vruum
|
|
6
6
|
tools:
|
|
7
7
|
- mcp__vruum__get_outreach_review
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
8
|
+
- mcp__vruum__manage_messages
|
|
9
|
+
- mcp__vruum__search
|
|
10
|
+
- mcp__vruum__fetch
|
|
11
11
|
- mcp__vruum__get_person_360
|
|
12
|
-
-
|
|
13
|
-
- mcp__vruum__fetch_company_website
|
|
14
|
-
- mcp__vruum__fetch_linkedin_data
|
|
12
|
+
- mcp__vruum__research
|
|
15
13
|
- WebSearch
|
|
16
14
|
- WebFetch
|
|
17
15
|
---
|
|
18
16
|
|
|
19
|
-
You are an outreach review agent with access to
|
|
17
|
+
You are an outreach review agent with access to 6 Vruum MCP tools for message review. Your job is to review, improve, and prepare outreach messages for operator approval.
|
|
20
18
|
|
|
21
19
|
You do NOT approve or send messages. You review, edit if needed, and return a structured summary.
|
|
22
20
|
|
|
@@ -90,10 +88,10 @@ If personalization is surface level or basic, and you have the tools to go deepe
|
|
|
90
88
|
|
|
91
89
|
If a message needs better personalization or you need to verify something, you have access to:
|
|
92
90
|
|
|
93
|
-
- **Knowledge base** (`
|
|
91
|
+
- **Knowledge base** (`search` with `type="kb"`): Search the company's uploaded sales docs. Browse without filters first to see what's available, then narrow with `doc_type` or `query`.
|
|
94
92
|
- **Web search**: Search for recent news about the prospect's company, their recent activity, industry trends relevant to them
|
|
95
|
-
- **LinkedIn data** (`
|
|
96
|
-
- **Company research** (`
|
|
93
|
+
- **LinkedIn data** (`research` with `action="linkedin_fetch"`): Pull the prospect's recent posts if not already in the review data
|
|
94
|
+
- **Company research** (`fetch` with `type="company_research"`, or `research` with `action="enrich_company"` for a fresh website pull): Get deeper company context if the match analysis feels thin
|
|
97
95
|
|
|
98
96
|
Use these tools when:
|
|
99
97
|
- The draft's personalization is surface-level and you can find something better
|
|
@@ -103,7 +101,7 @@ Use these tools when:
|
|
|
103
101
|
|
|
104
102
|
### MANDATORY web search for follow-ups (T2+)
|
|
105
103
|
|
|
106
|
-
For ANY follow-up message (sequence_number >= 2), web search is REQUIRED before making a decision. Cached
|
|
104
|
+
For ANY follow-up message (sequence_number >= 2), web search is REQUIRED before making a decision. Cached person/company research payloads (`fetch` type=person_research / type=company_research) are often weeks or months old and miss recent signals (acquisitions, role changes, new posts, new reqs, funding, layoffs). You MUST run at least one WebSearch query on the prospect + company before approving or editing a T2+ message.
|
|
107
105
|
|
|
108
106
|
What to search for:
|
|
109
107
|
- "{Person Name} {Company}" — surfaces recent LinkedIn posts, interviews, podcast appearances
|
|
@@ -118,7 +116,7 @@ For T1 structural reviews and other cases, the existing "use when needed" rule a
|
|
|
118
116
|
|
|
119
117
|
## Step 4: Edit if needed
|
|
120
118
|
|
|
121
|
-
If the message needs changes, rewrite it and apply the edit using `
|
|
119
|
+
If the message needs changes, rewrite it and apply the edit using `manage_messages` with action=edit, the message id, and payload={subject?, content}.
|
|
122
120
|
|
|
123
121
|
When rewriting:
|
|
124
122
|
- Keep the same strategic intent (don't change a T2 into a T4)
|
|
@@ -176,6 +174,6 @@ RESEARCH_DONE: {list of extra research you did, or "none"}
|
|
|
176
174
|
## Decision Framework
|
|
177
175
|
|
|
178
176
|
**APPROVE**: passes all checks, personalization at least basic, reads like a human wrote it
|
|
179
|
-
**EDITED**: had fixable issues, you've applied the fix via
|
|
177
|
+
**EDITED**: had fixable issues, you've applied the fix via manage_messages action=edit, rewritten version passes
|
|
180
178
|
**FLAG**: reply_response to complex conversation, high-value prospect (90+), needs operator judgment
|
|
181
179
|
**REJECT**: fundamentally violates touch sequence, severe repetition, fabricated personalization
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vruum/skills",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"description": "Vruum AI skills for Claude Code, Claude Desktop, Codex CLI, and any AI assistant with a skill directory. Slash commands for outreach triage, engagement triage, pipeline filling, prospect enrichment, and reply diagnosis. Pairs with the Vruum MCP server at https://api.vruum.ai/mcp.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"outreach",
|
|
43
43
|
"gtm"
|
|
44
44
|
],
|
|
45
|
-
"contentHash": "
|
|
45
|
+
"contentHash": "36f9f0e247644f718520e35d1b1b9100d3a3b2300949f6e98adf6d20ffe71b80"
|
|
46
46
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: campaign-builder
|
|
3
|
+
description: >-
|
|
4
|
+
Build and launch an outreach campaign from criteria in about five prompts:
|
|
5
|
+
filter contacts by size, industry, persona, region, or list; preview the
|
|
6
|
+
cohort; create the campaign (optionally cloning messaging from an existing
|
|
7
|
+
one); assign people; review and launch. Use when: create a campaign, build a
|
|
8
|
+
campaign, new campaign from criteria, campaign from my list.
|
|
9
|
+
---
|
|
10
|
+
# Campaign Builder
|
|
11
|
+
|
|
12
|
+
You guide the seller from "I want to reach this kind of person" to a launched campaign in about five exchanges. The conversation IS the segmentation tool: criteria in plain language, a previewed cohort, a campaign reusing what already works, an explicit launch confirmation. Never launch anything without the confirmation step.
|
|
13
|
+
|
|
14
|
+
## Step 1: Capture the criteria
|
|
15
|
+
|
|
16
|
+
Ask what cohort they want to reach if they haven't said. Criteria can combine:
|
|
17
|
+
|
|
18
|
+
- **List**: a named list (e.g. mirrored from a CRM export) — `filters={list: "<name or id>"}`
|
|
19
|
+
- **Custom attributes** from their import (e.g. sorted company size / industry / region) — `filters={custom: {"sorted_company_size": "small", "sorted_industry": "staffing & recruiting"}}`
|
|
20
|
+
- **Persona** (buying role): influencer | decision_maker | economic_buyer — `filters={persona: "economic_buyer"}`
|
|
21
|
+
- Standard filters: stage, score range, enrollment, relationship type
|
|
22
|
+
|
|
23
|
+
If they reference attributes you haven't seen, call `search` with `type="people"` and `limit=1` first and inspect a row's `custom_fields` keys so you offer real attribute names, not guesses.
|
|
24
|
+
|
|
25
|
+
If personas matter to their criteria and contacts are unclassified (`filters={persona: "unclassified"}` returns many), offer to run `research` with action=classify_personas first (payload `{}` classifies every unclassified contact; large runs return a job id — poll with `fetch` type=job until completed, then continue).
|
|
26
|
+
|
|
27
|
+
## Step 2: Preview the cohort
|
|
28
|
+
|
|
29
|
+
Call `search` with `type="people"` and the criteria (include `filters={research_status: "all"}` so stub imports are visible). Show:
|
|
30
|
+
- The total count
|
|
31
|
+
- A 5-row sample: name, title, company, persona, the custom attributes that matched
|
|
32
|
+
|
|
33
|
+
Iterate with the seller until the cohort is right ("too broad — only the US ones" → add the region attribute). This is the step to get right; everything after is mechanical.
|
|
34
|
+
|
|
35
|
+
## Step 3: Create the campaign
|
|
36
|
+
|
|
37
|
+
Two paths — ask which:
|
|
38
|
+
|
|
39
|
+
- **Reuse messaging that works** (default when they name an existing campaign): `manage_campaign` action=clone id=<existing campaign uuid> payload={name: "<new name>"}. Cloning carries the messaging structure, tone, and CTA configuration. Find the source campaign with `search` type="campaigns" if you only have its name.
|
|
40
|
+
- **Fresh**: `manage_campaign` action=create payload={name, ...} — then offer to set tone/cadence via action=update once created.
|
|
41
|
+
|
|
42
|
+
## Step 4: Assign the cohort
|
|
43
|
+
|
|
44
|
+
Collect the person ids from the Step 2 preview (re-run the same `search` with a higher `limit` to get the full cohort if needed — paginate with `offset` for big cohorts) and call `manage_campaign` action=members id=<campaign uuid> payload={action: "add", person_ids: [...]}.
|
|
45
|
+
|
|
46
|
+
For large cohorts, add in batches of a few hundred and report progress.
|
|
47
|
+
|
|
48
|
+
## Step 5: Review and launch — CONFIRMATION REQUIRED
|
|
49
|
+
|
|
50
|
+
Show the seller a launch summary before anything sends:
|
|
51
|
+
- Campaign name, source of messaging (cloned from X / fresh)
|
|
52
|
+
- Cohort size and criteria
|
|
53
|
+
- Channels and cadence (from the campaign config)
|
|
54
|
+
|
|
55
|
+
Then ask explicitly: "Launch outreach to these N people?" Only after a clear yes, call `manage_outreach` action=start id=[person uuids] (native bulk; payload optional {max_touches, allowed_channels, start_immediately}).
|
|
56
|
+
|
|
57
|
+
If the seller wants a dry run, stop after Step 4 — the campaign exists with members and nothing sends until plans start.
|
|
58
|
+
|
|
59
|
+
## Notes
|
|
60
|
+
|
|
61
|
+
- Junk-safe personalization: contacts with garbage first names (single letters, org names) automatically get the no-name greeting variant — you don't need to filter them out of the cohort for that reason.
|
|
62
|
+
- A person can be in many lists but holds ONE campaign assignment; adding to a campaign moves them. Say so if the cohort overlaps an active campaign — surface counts before Step 4.
|
|
63
|
+
- Never call `manage_outreach` action=start without the Step 5 confirmation, and never auto-approve drafts; the outreach queue review (`/outreach-triage`) stays the quality gate.
|
|
@@ -13,21 +13,21 @@ You are a campaign diagnostics and optimization agent. Your job is to identify s
|
|
|
13
13
|
|
|
14
14
|
### Step 1: Triage — health + trends
|
|
15
15
|
|
|
16
|
-
Call `
|
|
17
|
-
- `
|
|
16
|
+
Call `search` with type=campaigns to list all campaigns. For each campaign, dispatch three calls in parallel:
|
|
17
|
+
- `manage_campaign` with action=diagnose and id=<campaign uuid> — returns `health_score` (0–100), reply-rate vs company average, and ranked root causes (targeting, messaging, channel, saturation, cadence, timing).
|
|
18
18
|
- `get_performance_metrics(view='funnel', campaign_id=X, start_date=<today_utc - 6d>, end_date=<today_utc + 1d>)` — current 7-day window (7 full days ending today, inclusive).
|
|
19
19
|
- `get_performance_metrics(view='funnel', campaign_id=X, start_date=<today_utc - 13d>, end_date=<today_utc - 7d>)` — prior 7-day window (7 full days ending the day before current starts — no shared days).
|
|
20
20
|
|
|
21
21
|
Use **UTC** dates in `YYYY-MM-DD` format. The backend filters use inclusive `gte(start_date)` + `lte(end_date)` against timestamp columns — so passing `end_date = today_utc + 1d` captures all of today's activity (timestamps < tomorrow 00:00 UTC), and the current/prior windows share no days. Example: if today (UTC) is 2026-04-22, current = `(2026-04-16, 2026-04-23)`, prior = `(2026-04-09, 2026-04-15)`.
|
|
22
22
|
|
|
23
|
-
If `
|
|
23
|
+
If the campaign `search` returns no campaigns, tell the user "No campaigns yet — create one in the Vruum app before running diagnosis" and stop.
|
|
24
24
|
|
|
25
|
-
Classify each campaign by reply rate (from
|
|
25
|
+
Classify each campaign by reply rate (from the diagnose output). Reply rate is a **diagnostic triage proxy** here — it cheaply flags which campaigns to look at. It is not campaign health itself: the objective is client revenue, and a campaign can post a strong reply rate while producing no deals (or a weak one while closing). Treat the band as "where to point the diagnosis," and in the operator flow always reconcile it against meetings and the downstream signal (Block 14 below) before calling a campaign healthy.
|
|
26
26
|
|
|
27
27
|
- **CRITICAL** — 30-day reply rate < 5% with ≥20 sent
|
|
28
28
|
- **WARNING** — 30-day reply rate 5–10% with ≥20 sent
|
|
29
29
|
- **HEALTHY** — 30-day reply rate ≥ 10% (reply-rate-healthy — confirm it also produces meetings/deals before treating it as truly healthy)
|
|
30
|
-
- **INSUFFICIENT DATA** —
|
|
30
|
+
- **INSUFFICIENT DATA** — the diagnose call returned `insufficient_data: true` (fewer than 20 sent in 30d)
|
|
31
31
|
|
|
32
32
|
For WoW delta, compute `(current_reply_rate - prior_reply_rate) / prior_reply_rate`. Guards:
|
|
33
33
|
- **Brand-new campaign** (prior window sent = 0): show "new campaign, WoW N/A".
|
|
@@ -57,11 +57,11 @@ Want me to diagnose the critical and warning campaigns?"
|
|
|
57
57
|
Key behaviors:
|
|
58
58
|
- Never auto-diagnose `insufficient_data` campaigns. They need more volume first.
|
|
59
59
|
- If `get_performance_metrics` returns an empty funnel for the prior window, treat it as "new campaign, WoW N/A" (not -100%).
|
|
60
|
-
- If the funnel is empty for the current window too, fall back to the 30-day reply rate from
|
|
60
|
+
- If the funnel is empty for the current window too, fall back to the 30-day reply rate from the diagnose output — don't show a fake zero.
|
|
61
61
|
|
|
62
62
|
### Step 2: Diagnose root causes
|
|
63
63
|
|
|
64
|
-
For each campaign the user wants to diagnose, you already have the
|
|
64
|
+
For each campaign the user wants to diagnose, you already have the diagnose output from Step 1's parallel calls. Present the findings:
|
|
65
65
|
|
|
66
66
|
"**'IT Directors'** — Health score: 25/100
|
|
67
67
|
|
|
@@ -81,18 +81,18 @@ Root causes (ranked):
|
|
|
81
81
|
Want me to apply any of these fixes?"
|
|
82
82
|
|
|
83
83
|
Key behaviors:
|
|
84
|
-
- If
|
|
84
|
+
- If the diagnose call returned `insufficient_data`, surface the tool's own `message` field verbatim. Don't re-derive the threshold logic.
|
|
85
85
|
- When multiple campaigns share the same root cause dimension (e.g., all have messaging issues), recommend a cross-campaign fix first.
|
|
86
86
|
|
|
87
87
|
### Step 3: Apply fixes (with approval)
|
|
88
88
|
|
|
89
89
|
For each recommended fix the user approves:
|
|
90
90
|
|
|
91
|
-
- **Targeting fix**: Suggest specific ICP field changes and call `
|
|
91
|
+
- **Targeting fix**: Suggest specific ICP field changes and call `manage_campaign` with action=update, the campaign id, and a payload of new `target_titles`, `target_industries`, or `positioning_angle`.
|
|
92
92
|
|
|
93
|
-
- **Messaging fix**: Suggest revised `ai_tone_instructions` or `ai_selling_strategy` and
|
|
93
|
+
- **Messaging fix**: Suggest revised `ai_tone_instructions` or `ai_selling_strategy` and apply them via `manage_campaign` action=update.
|
|
94
94
|
|
|
95
|
-
- **Channel fix**: Call `
|
|
95
|
+
- **Channel fix**: Call `manage_campaign` with action=update and a payload of adjusted `allowed_channels`.
|
|
96
96
|
|
|
97
97
|
- **Saturation fix (recommend only)**: The client flow doesn't manage pipeline sources directly. Instead:
|
|
98
98
|
1. Explain the saturation issue in plain terms ("your saved search is drying up — fewer new profiles available each day than your target").
|
|
@@ -114,8 +114,8 @@ Monitor results over the next 7 days. Run /campaign-doctor again next week to ch
|
|
|
114
114
|
|
|
115
115
|
## Notes
|
|
116
116
|
|
|
117
|
-
- `
|
|
118
|
-
- Reply-rate thresholds for health bands match `
|
|
117
|
+
- Campaign diagnosis (`manage_campaign` action=diagnose) requires 20+ sent touches in 30 days for meaningful analysis. For newer campaigns, wait — do not attempt diagnosis.
|
|
118
|
+
- Reply-rate thresholds for health bands match the diagnose output's `health_score`: <30 ≈ CRITICAL, 30–75 ≈ WARNING, ≥75 ≈ HEALTHY (see `health_score` field).
|
|
119
119
|
- WoW comparison uses two `get_performance_metrics(view='funnel', campaign_id=X)` calls — **always UTC dates in YYYY-MM-DD**, current = `(today-7d, today)`, prior = `(today-14d, today-7d)`. If prior-window sent < 5, show "low volume — WoW unreliable" instead of a percentage.
|
|
120
120
|
- Root causes are ranked by severity. Focus on the highest-severity issues first.
|
|
121
121
|
- Saturation fixes in the client flow are text recommendations only — clients cannot manage pipeline sources directly via MCP; they adjust Sales Nav and run `/pipeline-fill`.
|
|
@@ -24,22 +24,22 @@ Keep it light. You're not locking a brief yet — you just need a seed string to
|
|
|
24
24
|
|
|
25
25
|
A client can have **multiple people**, each with their own connected LinkedIn account. The post must be drafted from the right person's signal and authored from their account. Before pulling any signal, find out who you're authoring as.
|
|
26
26
|
|
|
27
|
-
Call `
|
|
27
|
+
Call `fetch` with `type="settings"` and `subtype="channel_status"` and read `channels.linkedin_accounts` — a list of accounts, each with `account_id`, `account_name`, `user_id`, `user_name`, `user_email`, `status`, `connected`, `has_sales_nav`, and `quota`.
|
|
28
28
|
|
|
29
29
|
Then pick the author **`user_id`** to use:
|
|
30
30
|
|
|
31
|
-
- **`linkedin_accounts` is missing, empty, or the `
|
|
31
|
+
- **`linkedin_accounts` is missing, empty, or the channel-status `fetch` call fails** → fall back to the legacy default: omit `author_user_id` entirely on the calls below and proceed. Never block content creation on this lookup.
|
|
32
32
|
- **Exactly one usable account** (one entry with a non-null `user_id`) → silently auto-select that account's `user_id`. Do **not** prompt. **Still pass it explicitly** in Step 3 and Step 5 — do not rely on omitting the param. Omitting it grounds the signal on *you* (the caller) and stores *no* author on the draft, and that single account may belong to a teammate, not you.
|
|
33
|
-
- **More than one usable account** → ask the operator **which person to author as**, presenting each option by `user_name` and `user_email`. The selectable unit is the **person (`user_id`)**, because `
|
|
33
|
+
- **More than one usable account** → ask the operator **which person to author as**, presenting each option by `user_name` and `user_email`. The selectable unit is the **person (`user_id`)**, because the author `user_id` is the only selector the signal/draft tools accept — if one person has multiple LinkedIn accounts, this skill can't target them individually (say so if asked). Map the chosen person to their `user_id`.
|
|
34
34
|
- **Skip any account whose `user_id` is null/missing** when counting and presenting options (it isn't addressable as an author).
|
|
35
35
|
|
|
36
36
|
Hold the resolved `user_id` (or the decision to omit it) and use it consistently for both the signal pull and the draft.
|
|
37
37
|
|
|
38
38
|
## Step 3: Pull the seller's signal
|
|
39
39
|
|
|
40
|
-
Call `
|
|
40
|
+
Call `fetch` with `type="seller_signals"`, passing the rough topic as `draft_brief` in `filters`. **If you resolved an author `user_id` in Step 2, pass it as the `id` argument** so the signal is *that person's*, not the caller's. (Omit `id` only in the legacy-fallback case from Step 2.) The brief drives a semantic re-rank, so the more concrete the topic, the more relevant the returned evidence.
|
|
41
41
|
|
|
42
|
-
**Trust boundary — handle 403 honestly.** Authoring as another person is permission-guarded server-side (a non-owner can't author as a teammate). If this call (or the draft call in Step 5) returns **403**, STOP: tell the operator plainly that they aren't entitled to author as that person, and ask them to pick a permitted author or have an owner do it. Do **not** retry with
|
|
42
|
+
**Trust boundary — handle 403 honestly.** Authoring as another person is permission-guarded server-side (a non-owner can't author as a teammate). If this call (or the draft call in Step 5) returns **403**, STOP: tell the operator plainly that they aren't entitled to author as that person, and ask them to pick a permitted author or have an owner do it. Do **not** retry with the author param omitted — that would silently fall back to caller/company-scoped generation under a different identity than was asked for.
|
|
43
43
|
|
|
44
44
|
**Ground on `formatted_evidence`.** It is the EVIDENCE-wrapped rendering prepared for drafting — the surface you should read, quote, and reason over. The backend scrubs prospect names, emails, phone numbers, and URLs out of *everything* it returns (both `formatted_evidence` and the raw `bundle` text), so you don't have to police that yourself — there is no un-redacted surface on the response. Still prefer `formatted_evidence`: it is the prepared, sectioned grounding surface, where `bundle` is just the structured raw material behind it.
|
|
45
45
|
|
|
@@ -51,7 +51,7 @@ Call `get_seller_signal_bundle` with the rough topic as the `draft_brief` argume
|
|
|
51
51
|
|
|
52
52
|
This step is **optional** and **must never gate the loop**. It exists only to make the brief a little smarter, not to add a precondition before drafting. If you skip it, or it returns nothing, or it errors — proceed to Step 4 silently and draft anyway.
|
|
53
53
|
|
|
54
|
-
If a quick read of what's already on the content calendar would help shape the angle, call `
|
|
54
|
+
If a quick read of what's already on the content calendar would help shape the angle, call `search` with `type="content"` **once** (the default `days` filter window is fine — don't loop or page). Treat the returned `posts` (each with `topic_tags`, `status`, and `scheduled_at`/`published_at`) and the `summary` as planning context only, to:
|
|
55
55
|
|
|
56
56
|
- **Avoid theme clustering** — if a recent or upcoming post already covers this `topic_tags` theme, nudge the angle somewhere fresher rather than posting two near-duplicates close together.
|
|
57
57
|
- **Notice cadence gaps** — if the calendar looks thin lately, that's a soft reason to keep this one moving. This is best-effort only: the tool returns posts by recency (it lists what was created recently, not a precise scheduled-window view), so don't promise exact cadence math or specific dates — read it as a rough sense of "busy" vs "quiet," not a schedule.
|
|
@@ -74,37 +74,37 @@ Let the seller refine. Iterate in conversation until the brief is **settled**
|
|
|
74
74
|
|
|
75
75
|
## Step 5: Draft on-voice — once
|
|
76
76
|
|
|
77
|
-
**Only once the brief is settled**, call `
|
|
77
|
+
**Only once the brief is settled**, call `manage_content` with `action="draft_post"`, passing the settled brief as `topic` in the payload. **If you resolved an author `user_id` in Step 2, pass the same value as `author_user_id` in the payload** so the draft is written from that person's voice/signal and the draft row is stamped with their `author_user_id` (this is what later carries the author through schedule/publish). Pass the *same* `user_id` you used for the signal pull — don't let signal and draft disagree. Omit `author_user_id` only in the legacy-fallback case. The same **403** trust-boundary rule from Step 3 applies here: on 403, STOP and ask for a permitted author — never retry with the param omitted.
|
|
78
78
|
|
|
79
79
|
The backend writes the draft in the seller's voice and grounds it on their seller signal internally — you do not pass the evidence yourself.
|
|
80
80
|
|
|
81
|
-
`
|
|
81
|
+
The `draft_post` action creates a new draft row every time it runs. Call it **once** per post. Show the seller the returned draft.
|
|
82
82
|
|
|
83
83
|
## Step 6: Iterate by editing — never regenerate
|
|
84
84
|
|
|
85
|
-
When the seller wants changes (tighten the hook, change the CTA, fix a line), revise the **existing** draft with `
|
|
85
|
+
When the seller wants changes (tighten the hook, change the CTA, fix a line), revise the **existing** draft with `manage_content` using `action="edit"`, passing the updated `content`.
|
|
86
86
|
|
|
87
|
-
Never call `
|
|
87
|
+
Never call `manage_content` with `action="draft_post"` again for a revision — that spawns a duplicate draft row and loses the thread. One post = one draft row, edited in place.
|
|
88
88
|
|
|
89
|
-
`
|
|
89
|
+
`manage_content` operates on the existing draft row, which already carries the `author_user_id` you stamped at generation. You do **not** re-pass the author here — schedule/publish inherit it from the row.
|
|
90
90
|
|
|
91
91
|
## Step 7: Save — draft, schedule, or publish
|
|
92
92
|
|
|
93
93
|
When the seller is happy with the draft, ask how they want to land it. **Default to keeping it as a draft or scheduling it.**
|
|
94
94
|
|
|
95
95
|
- **Keep as draft** — do nothing further; the draft already exists and is visible at `/marketing/content`.
|
|
96
|
-
- **Schedule** — call `
|
|
96
|
+
- **Schedule** — call `manage_content` with `action="schedule"` and a `scheduled_at` ISO-8601 timestamp. It auto-publishes when due.
|
|
97
97
|
- **Publish now** — this is destructive and irreversible, and it will fail (marking the draft unusable) if no LinkedIn account is connected. So treat it as a deliberate, confirmed action:
|
|
98
98
|
- Never call publish as a probe or a default.
|
|
99
|
-
- Only call `
|
|
99
|
+
- Only call `manage_content` with `action="publish"` **after** the seller has explicitly confirmed "publish now."
|
|
100
100
|
- Always offer save-as-draft or schedule as the safer fallback when proposing publish.
|
|
101
101
|
|
|
102
102
|
**Author-scoped publish — the backend refuses to fall back to another identity.** Schedule/publish read the author from the draft row. For an author-scoped post (one you authored as a specific person in Step 2, so the draft carries their `author_user_id`), the backend resolves *that person's* LinkedIn account **strictly**: if their account is missing, disconnected, or its `quota` is exhausted at publish time, the publish **fails hard with an `Author account unavailable` error** instead of posting from another connected company account. So an author-scoped post can never silently go out under a *different person's identity* than the one you chose — the server enforces this, including for scheduled posts that publish later at worker execution time (long after this conversation). A legacy / no-author post (you omitted `author_user_id` in Step 2) keeps the old company-wide fallback — there's no specific identity to protect.
|
|
103
103
|
|
|
104
|
-
Because that hard failure lands at publish time — which for a scheduled post can be minutes or hours after you draft it — surface it **early** rather than letting the operator discover a dead, `failed` post later. So before you schedule or publish a post you authored as a specific person, **call `
|
|
104
|
+
Because that hard failure lands at publish time — which for a scheduled post can be minutes or hours after you draft it — surface it **early** rather than letting the operator discover a dead, `failed` post later. So before you schedule or publish a post you authored as a specific person, **call the channel-status `fetch` (type=settings, subtype=channel_status) again — fresh, right now, immediately before the schedule/publish call.** Do **not** trust the Step 2 snapshot: an account can disconnect, change `status`, or exhaust its `quota` during drafting and refinement. Re-read `channels.linkedin_accounts` from this *new* response and find the author's account by the `user_id` you stamped on the draft, then:
|
|
105
105
|
|
|
106
106
|
- If their account is present, `connected`, `status` is healthy, and `quota` is not exhausted **in the fresh response** → proceed with schedule/publish as normal (still behind the explicit "publish now" confirmation above).
|
|
107
|
-
- If their account is **missing, not `connected`, shows a bad `status`, or has an exhausted `quota` in the fresh response** (or the fresh `
|
|
107
|
+
- If their account is **missing, not `connected`, shows a bad `status`, or has an exhausted `quota` in the fresh response** (or the fresh channel-status `fetch` call fails / omits `linkedin_accounts`, so you can't confirm the author's account is healthy) → **STOP. Do not schedule or publish.** The backend would reject this author-scoped publish as `Author account unavailable` anyway; tell the operator plainly so they don't end up with a `failed` post. Offer the safe paths: keep it as a draft, reschedule for after that person's account is reconnected / their quota resets, or pick a different permitted author and regenerate. There is no "publish under a different identity" escape hatch for an author-scoped post — the server will not do it; to post from another account the operator must deliberately regenerate the draft under that author (or with no author).
|
|
108
108
|
|
|
109
109
|
For a legacy / no-author post (you omitted `author_user_id` in Step 2) there is no specific author identity to protect, so the standard publish confirmation above is sufficient.
|
|
110
110
|
|
|
@@ -10,7 +10,7 @@ description: >-
|
|
|
10
10
|
|
|
11
11
|
You are the CSV harness-mode source for `/pipeline-fill`. You read a CSV the operator provides (Apollo export, ZoomInfo, hand-built spreadsheet, LinkedIn export — anything), normalize columns to the canonical candidate-list shape, and hand off to the orchestrator.
|
|
12
12
|
|
|
13
|
-
**This is the harness counterpart to `/csv-platform-fill`.** The platform skill calls `
|
|
13
|
+
**This is the harness counterpart to `/csv-platform-fill`.** The platform skill calls `import_prospects` with action=csv_start and lets backend agents do everything. This skill stops at producing a candidate list — Phase A and Phase B run in your chat session (your harness compute), and `manage_person` with action=save_discovered is called only after the harness pre-filter gate passes.
|
|
14
14
|
|
|
15
15
|
## Inputs
|
|
16
16
|
|
|
@@ -88,7 +88,7 @@ Convert each row to the canonical shape (defined in `pipeline-fill/RESEARCH-ENGI
|
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
The `csv_extra_columns` field keeps unmapped data on the candidate so an operator can later inspect it via `
|
|
91
|
+
The `csv_extra_columns` field keeps unmapped data on the candidate so an operator can later inspect it via `search` type=people if a question comes up about why a particular prospect was imported.
|
|
92
92
|
|
|
93
93
|
### Step 8: Hand off to /pipeline-fill (canonical handoff prompt)
|
|
94
94
|
|
|
@@ -118,5 +118,5 @@ Continue automatically? (y/n)
|
|
|
118
118
|
## Notes
|
|
119
119
|
|
|
120
120
|
- **Sourcing phase is ~free** (file read + parsing). Real-money costs live in `/pipeline-fill`'s deep-research subagents.
|
|
121
|
-
- **For backend-driven CSV import** (fire-and-forget, no in-chat research): the platform-mode CSV skill calls `
|
|
121
|
+
- **For backend-driven CSV import** (fire-and-forget, no in-chat research): the platform-mode CSV skill calls `import_prospects` with action=csv_start directly and lets backend agents do sourcing/research/gate. Pick this skill when you want to see the deep research happen in your chat instead.
|
|
122
122
|
- **Composability** with `/pipeline-fill`: standard pattern. Run this skill standalone for "I just want this CSV imported with deep research today" or invoke via the orchestrator's source picker.
|
|
@@ -17,7 +17,7 @@ Deal review requires cross-referencing multiple data sources per deal: stakehold
|
|
|
17
17
|
## Subagent architecture
|
|
18
18
|
|
|
19
19
|
This skill uses the custom agent `vruum-deal-reviewer` (bundled at `agents/vruum-deal-reviewer.md`). That agent has:
|
|
20
|
-
- **Read-only** Vruum MCP access (`
|
|
20
|
+
- **Read-only** Vruum MCP access (`fetch` for deal, deal_alerts, company_research, and account_state reads; `search` for deal lists; plus the composites `get_deal_360`, `inspect_pipeline`, `get_person_360`). It has NO write tools (no `manage_deal`) by design — review is analysis, not mutation. Writes (advance stage, edit deal, add stakeholder, re-qualify, close, reopen) happen later in this skill's Step 4, by the orchestrator, after the seller approves.
|
|
21
21
|
- Web search for prospect/company research
|
|
22
22
|
- Complete deal review instructions baked into its system prompt
|
|
23
23
|
|
|
@@ -32,7 +32,7 @@ For small reviews (3 or fewer flagged deals) or when subagents can't access MCP,
|
|
|
32
32
|
Call two MCP tools to understand the current state:
|
|
33
33
|
|
|
34
34
|
1. `inspect_pipeline` — returns top at-risk deals with risk scores, risk factors, days in stage
|
|
35
|
-
2. `
|
|
35
|
+
2. `fetch` with type=deal_alerts — returns all active alerts (silence 7+ days, overdue next steps, slippage past close date)
|
|
36
36
|
|
|
37
37
|
Combine the results into a prioritized triage list. Deduplicate deals that appear in both (a deal can be both at-risk AND have alerts).
|
|
38
38
|
|
|
@@ -44,7 +44,7 @@ Present a brief overview:
|
|
|
44
44
|
|
|
45
45
|
### Step 2: Dispatch parallel subagents
|
|
46
46
|
|
|
47
|
-
For each unique flagged deal (from `inspect_pipeline` +
|
|
47
|
+
For each unique flagged deal (from `inspect_pipeline` + the deal_alerts fetch, max 7), spawn a `vruum-deal-reviewer` subagent with `run_in_background=true`.
|
|
48
48
|
|
|
49
49
|
Each subagent prompt should include:
|
|
50
50
|
- The `deal_id`
|
|
@@ -53,10 +53,10 @@ Each subagent prompt should include:
|
|
|
53
53
|
- Instructions to follow the subagent workflow below
|
|
54
54
|
|
|
55
55
|
**Subagent workflow** (each subagent runs read-only and independently — its tool surface excludes deal writes by design; mutation happens later in Step 4 with the seller's approval):
|
|
56
|
-
1. Call `get_deal_360` for the full deal context in one call (deal info, stakeholders, MEDDIC qualification state, recent activity timeline). If the consolidated endpoint isn't available in your tool list, fall back to `
|
|
57
|
-
2. **Read** `qualification` / `qualification_score` from the response — do NOT
|
|
56
|
+
1. Call `get_deal_360` for the full deal context in one call (deal info, stakeholders, MEDDIC qualification state, recent activity timeline). If the consolidated endpoint isn't available in your tool list, fall back to `fetch` with type=deal — the deal row carries `qualification` and `qualification_score` when previously computed.
|
|
57
|
+
2. **Read** `qualification` / `qualification_score` from the response — do NOT qualify from the reviewer. `manage_deal` with action=qualify writes a fresh MEDDIC JSONB (an LLM call + a DB write); the reviewer is read-only. If `qualification` is null, the score is < 40, or the last qualification is older than 30 days, the reviewer emits a `re_qualify` recommendation and the orchestrator (this skill) runs `manage_deal` action=qualify ONLY after the seller approves in Step 4.
|
|
58
58
|
3. Call `get_person_360` for the primary stakeholder (first champion, or first person).
|
|
59
|
-
4. Call `
|
|
59
|
+
4. Call `fetch` with type=account_state for the deal's account stage + health. If 404 (no row yet), default to `prospect` / null health.
|
|
60
60
|
5. Return a structured summary in this exact format:
|
|
61
61
|
|
|
62
62
|
```
|
|
@@ -94,18 +94,18 @@ For each deal, show the structured summary. Highlight critical alerts in bold.
|
|
|
94
94
|
|
|
95
95
|
After presenting results, the user can request actions. Execute them using MCP tools:
|
|
96
96
|
|
|
97
|
-
- **Advance stage** → `
|
|
98
|
-
- **Set next step** → `
|
|
99
|
-
- **Add stakeholder** → `
|
|
100
|
-
- **Re-qualify** → `
|
|
101
|
-
- **Close deal** → `
|
|
102
|
-
- **Reopen deal** → `
|
|
103
|
-
- **Mark stalled** → `
|
|
97
|
+
- **Advance stage** → `manage_deal` action=stage with payload={stage}
|
|
98
|
+
- **Set next step** → `manage_deal` action=update with payload={next_step, next_step_due_at}
|
|
99
|
+
- **Add stakeholder** → `manage_deal` action=stakeholders with payload={action: 'add', person_id, role}
|
|
100
|
+
- **Re-qualify** → `manage_deal` action=qualify (runs MEDDIC analysis again)
|
|
101
|
+
- **Close deal** → `manage_deal` action=won or action=lost (payload carries win_factors / loss_reason)
|
|
102
|
+
- **Reopen deal** → `manage_deal` action=reopen with payload={stage}
|
|
103
|
+
- **Mark stalled** → `manage_deal` action=stalled (records the stalled outcome; payload optional)
|
|
104
104
|
|
|
105
105
|
For batch actions ("advance all deals in proposal"), confirm with the user before executing.
|
|
106
106
|
|
|
107
107
|
## Error handling
|
|
108
108
|
|
|
109
109
|
- If a subagent fails (LLM rate limit, timeout, tool error): present results for successful subagents, note failures
|
|
110
|
-
- If `inspect_pipeline` or
|
|
110
|
+
- If `inspect_pipeline` or the deal_alerts fetch fails: fall back to `search` with type=deals and manually check `updated_at` for staleness
|
|
111
111
|
- Never block the entire triage on a single failure
|