@sellable/mcp 0.1.415 → 0.1.416
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/README.md +5 -2
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/tools/refill-sends.d.ts +21 -2
- package/dist/tools/refill-sends.js +47 -2
- package/package.json +1 -1
- package/skills/create-campaign-v2/references/ai-tells.md +0 -6
- package/skills/create-campaign-v2/references/gold-standard-message-examples.md +7 -34
- package/skills/create-campaign-v2/references/gold-standard-message-patterns.md +1 -11
- package/skills/create-campaign-v2/references/sellable-cleanup-rules.md +1 -1
- package/skills/create-evergreen-campaigns/SKILL.md +34 -455
- package/skills/generate-messages/SKILL.md +33 -94
- package/skills/refill-sends/SKILL.md +15 -8
package/README.md
CHANGED
|
@@ -66,8 +66,9 @@ reader value, validates proof/AI tells, and saves content artifacts under
|
|
|
66
66
|
|
|
67
67
|
The refill-sends public command wrapper plans and executes approval-gated send
|
|
68
68
|
refills. It supports `--yolo` and optional sender selectors such as
|
|
69
|
-
`--sender "Christian Reyes"
|
|
70
|
-
|
|
69
|
+
`--sender "Christian Reyes"`, optional through-date selectors such as
|
|
70
|
+
`--until 2026-06-30`, or typed MCP calls to `refill_sends({ yolo, senders,
|
|
71
|
+
untilDate })` from:
|
|
71
72
|
|
|
72
73
|
- `mcp/sellable/skills/refill-sends/SKILL.md`
|
|
73
74
|
|
|
@@ -296,6 +297,7 @@ Use the refill command for sender send refills:
|
|
|
296
297
|
|
|
297
298
|
```
|
|
298
299
|
/sellable:refill-sends --yolo
|
|
300
|
+
/sellable:refill-sends --yolo --until 2026-06-30
|
|
299
301
|
/sellable:refill-sends --sender "Christian Reyes" --sender "Thomas Nobbs"
|
|
300
302
|
```
|
|
301
303
|
|
|
@@ -323,6 +325,7 @@ Use the Codex refill command with the same flags:
|
|
|
323
325
|
|
|
324
326
|
```
|
|
325
327
|
$sellable:refill-sends --yolo
|
|
328
|
+
$sellable:refill-sends --yolo --until 2026-06-30
|
|
326
329
|
$sellable:refill-sends --sender "Christian Reyes" --sender "Thomas Nobbs"
|
|
327
330
|
```
|
|
328
331
|
|
package/dist/index-dev.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
|
@@ -6,6 +6,7 @@ type RefillSendsCommandInput = {
|
|
|
6
6
|
senderIds?: string[];
|
|
7
7
|
senderNames?: string[];
|
|
8
8
|
horizonSendDays?: number;
|
|
9
|
+
untilDate?: string;
|
|
9
10
|
campaignId?: string;
|
|
10
11
|
tableId?: string;
|
|
11
12
|
intent?: RefillSendsIntent;
|
|
@@ -48,6 +49,11 @@ export declare const refillSendsToolDefinitions: {
|
|
|
48
49
|
maximum: number;
|
|
49
50
|
description: string;
|
|
50
51
|
};
|
|
52
|
+
untilDate: {
|
|
53
|
+
type: string;
|
|
54
|
+
pattern: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
51
57
|
campaignId: {
|
|
52
58
|
type: string;
|
|
53
59
|
description: string;
|
|
@@ -78,7 +84,19 @@ export declare function refillSendsCommand(input?: RefillSendsCommandInput): {
|
|
|
78
84
|
workflowPromptName: string;
|
|
79
85
|
yolo: boolean;
|
|
80
86
|
intent: RefillSendsIntent;
|
|
81
|
-
|
|
87
|
+
untilDate: string | null;
|
|
88
|
+
horizonSendDays: number | null;
|
|
89
|
+
fillWindow: {
|
|
90
|
+
mode: string;
|
|
91
|
+
untilDate: string;
|
|
92
|
+
description: string;
|
|
93
|
+
horizonSendDays?: undefined;
|
|
94
|
+
} | {
|
|
95
|
+
mode: string;
|
|
96
|
+
horizonSendDays: number | null;
|
|
97
|
+
description: string;
|
|
98
|
+
untilDate?: undefined;
|
|
99
|
+
};
|
|
82
100
|
approvalMode: RefillSendsApprovalMode;
|
|
83
101
|
campaignId: string | null;
|
|
84
102
|
tableId: string | null;
|
|
@@ -101,7 +119,8 @@ export declare function refillSendsCommand(input?: RefillSendsCommandInput): {
|
|
|
101
119
|
senders: string[];
|
|
102
120
|
senderIds: string[];
|
|
103
121
|
senderNames: string[];
|
|
104
|
-
|
|
122
|
+
untilDate: string | null;
|
|
123
|
+
horizonSendDays: number | null;
|
|
105
124
|
campaignId: string | undefined;
|
|
106
125
|
tableId: string | undefined;
|
|
107
126
|
intent: RefillSendsIntent;
|
|
@@ -14,6 +14,25 @@ function normalizeHorizonSendDays(value) {
|
|
|
14
14
|
}
|
|
15
15
|
return Math.max(1, Math.min(7, Math.floor(value)));
|
|
16
16
|
}
|
|
17
|
+
function normalizeUntilDate(value) {
|
|
18
|
+
if (value === undefined || value === null || value === "")
|
|
19
|
+
return null;
|
|
20
|
+
if (typeof value !== "string") {
|
|
21
|
+
throw new Error("untilDate must be a string in YYYY-MM-DD format.");
|
|
22
|
+
}
|
|
23
|
+
const trimmed = value.trim();
|
|
24
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
|
|
25
|
+
throw new Error("untilDate must use YYYY-MM-DD format.");
|
|
26
|
+
}
|
|
27
|
+
const [year, month, day] = trimmed.split("-").map((part) => Number(part));
|
|
28
|
+
const parsed = new Date(Date.UTC(year, month - 1, day));
|
|
29
|
+
if (parsed.getUTCFullYear() !== year ||
|
|
30
|
+
parsed.getUTCMonth() !== month - 1 ||
|
|
31
|
+
parsed.getUTCDate() !== day) {
|
|
32
|
+
throw new Error("untilDate must be a valid calendar date.");
|
|
33
|
+
}
|
|
34
|
+
return trimmed;
|
|
35
|
+
}
|
|
17
36
|
export const refillSendsToolDefinitions = [
|
|
18
37
|
{
|
|
19
38
|
name: "refill_sends",
|
|
@@ -44,7 +63,12 @@ export const refillSendsToolDefinitions = [
|
|
|
44
63
|
type: "number",
|
|
45
64
|
minimum: 1,
|
|
46
65
|
maximum: 7,
|
|
47
|
-
description: "Number of sender-local send days to fill. Defaults to 2.",
|
|
66
|
+
description: "Number of sender-local send days to fill. Defaults to 2 when untilDate is not provided.",
|
|
67
|
+
},
|
|
68
|
+
untilDate: {
|
|
69
|
+
type: "string",
|
|
70
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
71
|
+
description: "Optional sender-local YYYY-MM-DD date to fill through, inclusive. Overrides the default two send-day horizon.",
|
|
48
72
|
},
|
|
49
73
|
campaignId: {
|
|
50
74
|
type: "string",
|
|
@@ -76,7 +100,10 @@ export function refillSendsCommand(input = {}) {
|
|
|
76
100
|
const senderNames = normalizeStrings(input.senderNames);
|
|
77
101
|
const hasSenderSelectors = senders.length > 0 || senderIds.length > 0 || senderNames.length > 0;
|
|
78
102
|
const yolo = input.yolo === true;
|
|
79
|
-
const
|
|
103
|
+
const untilDate = normalizeUntilDate(input.untilDate);
|
|
104
|
+
const horizonSendDays = untilDate
|
|
105
|
+
? null
|
|
106
|
+
: normalizeHorizonSendDays(input.horizonSendDays);
|
|
80
107
|
const intent = input.intent ?? "plain";
|
|
81
108
|
const approvalMode = input.approvalMode ?? (yolo ? "approve" : "mark_ready");
|
|
82
109
|
const senderScope = hasSenderSelectors
|
|
@@ -91,7 +118,19 @@ export function refillSendsCommand(input = {}) {
|
|
|
91
118
|
workflowPromptName: "refill-sends-workflow",
|
|
92
119
|
yolo,
|
|
93
120
|
intent,
|
|
121
|
+
untilDate,
|
|
94
122
|
horizonSendDays,
|
|
123
|
+
fillWindow: untilDate
|
|
124
|
+
? {
|
|
125
|
+
mode: "until_date",
|
|
126
|
+
untilDate,
|
|
127
|
+
description: "Fill through this sender-local date inclusive, skipping no-send days and never extending beyond the date without a new packet.",
|
|
128
|
+
}
|
|
129
|
+
: {
|
|
130
|
+
mode: "horizon_send_days",
|
|
131
|
+
horizonSendDays,
|
|
132
|
+
description: "Fill the default bounded horizon by sender-local send days.",
|
|
133
|
+
},
|
|
95
134
|
approvalMode,
|
|
96
135
|
campaignId: input.campaignId ?? null,
|
|
97
136
|
tableId: input.tableId ?? null,
|
|
@@ -105,6 +144,9 @@ export function refillSendsCommand(input = {}) {
|
|
|
105
144
|
'Load get_subskill_prompt({ subskillName: "refill-sends-workflow" }) before any product operation.',
|
|
106
145
|
`Resolve route with resolve_campaign_fill_route({ intent: "${intent}"${input.campaignId ? `, campaignId: "${input.campaignId}"` : ""}${input.tableId ? `, tableId: "${input.tableId}"` : ""} }).`,
|
|
107
146
|
"Call list_senders and get_sender_routing, then resolve sender selectors against active enrolled campaign-backed sequence senders.",
|
|
147
|
+
untilDate
|
|
148
|
+
? `Use fill window untilDate="${untilDate}" as the inclusive sender-local through date; do not extend beyond that date without a new approval packet.`
|
|
149
|
+
: `Use the default fill window of ${horizonSendDays} sender-local send days.`,
|
|
108
150
|
"Read get_campaign_refill_state for enough exact candidate campaigns to pick the best per-sender target by recent/future scheduler-owned sends, then recent result evidence, then source health.",
|
|
109
151
|
"Fresh reread get_campaign_refill_state immediately before any import, prep, approval, or horizon-fill mutation.",
|
|
110
152
|
],
|
|
@@ -125,10 +167,12 @@ export function refillSendsCommand(input = {}) {
|
|
|
125
167
|
hostExamples: {
|
|
126
168
|
claude: [
|
|
127
169
|
"/sellable:refill-sends --yolo",
|
|
170
|
+
"/sellable:refill-sends --yolo --until 2026-06-30",
|
|
128
171
|
'/sellable:refill-sends --sender "Christian Reyes" --sender "Thomas Nobbs"',
|
|
129
172
|
],
|
|
130
173
|
codex: [
|
|
131
174
|
"$sellable:refill-sends --yolo",
|
|
175
|
+
"$sellable:refill-sends --yolo --until 2026-06-30",
|
|
132
176
|
'$sellable:refill-sends --sender "Christian Reyes" --sender "Thomas Nobbs"',
|
|
133
177
|
],
|
|
134
178
|
mcpTool: {
|
|
@@ -138,6 +182,7 @@ export function refillSendsCommand(input = {}) {
|
|
|
138
182
|
senders,
|
|
139
183
|
senderIds,
|
|
140
184
|
senderNames,
|
|
185
|
+
untilDate,
|
|
141
186
|
horizonSendDays,
|
|
142
187
|
campaignId: input.campaignId,
|
|
143
188
|
tableId: input.tableId,
|
package/package.json
CHANGED
|
@@ -55,12 +55,6 @@ raise your hand for X (creepy to reach out based on that, i know) - but
|
|
|
55
55
|
this felt too on the nose to ignore"` shapes only when the source was an
|
|
56
56
|
explicit lead-magnet comment, reply, or opt-in.
|
|
57
57
|
|
|
58
|
-
Shared evergreen exception: for Shared Signal Discovery and other shared
|
|
59
|
-
evergreen lanes, the weak-signal permissioned bridge above is still a tell.
|
|
60
|
-
Reject `hope this is relevant`, `might be interested`, and `saw you in a few
|
|
61
|
-
conversations` as openers; require a concrete signal/problem bridge or omit the
|
|
62
|
-
source line.
|
|
63
|
-
|
|
64
58
|
**Severity:** REJECT
|
|
65
59
|
|
|
66
60
|
## Tell #3 — Explicit date or duration references about the recipient
|
|
@@ -203,32 +203,6 @@ Why it works:
|
|
|
203
203
|
**Proof ranks used:** rank 1 (mechanism - 1,000+ conditions from one blood
|
|
204
204
|
draw), rank 3 (risk before claims).
|
|
205
205
|
|
|
206
|
-
Do not use the Superpower weak-signal hedge pattern for Shared Evergreen Signal Discovery. Shared evergreen lanes are reused across multiple senders and must not open with `hope this is relevant`, `might be interested`, or `saw you in a few conversations`. Use the safe pattern below instead.
|
|
207
|
-
|
|
208
|
-
### Shared Evergreen Signal Discovery Safe Pattern
|
|
209
|
-
|
|
210
|
-
Use when the campaign is a shared evergreen signal lane and the source signal is
|
|
211
|
-
real but not sender-owned.
|
|
212
|
-
|
|
213
|
-
```text
|
|
214
|
-
Hey {{first_name}},
|
|
215
|
-
|
|
216
|
-
The AI-assisted GTM conversations keep coming back to one problem: turning good LinkedIn signal into review-ready pipeline without another outbound dashboard.
|
|
217
|
-
|
|
218
|
-
Sellable helps teams find signal-matched prospects, filter for fit, and generate reviewed LinkedIn copy from one workflow.
|
|
219
|
-
|
|
220
|
-
Curious if LinkedIn outbound is a channel you're trying to make more reliable this quarter?
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Why it works:
|
|
224
|
-
|
|
225
|
-
- no low-confidence relevance hedge
|
|
226
|
-
- no `I'm building` or founder-only first person
|
|
227
|
-
- no internal workflow vocabulary such as Codex, MCP, agent workflows, workflow
|
|
228
|
-
table, lead source, or signal discovery
|
|
229
|
-
- the signal becomes a buyer problem instead of an activity-log opener
|
|
230
|
-
- safe for multiple senders attached to the same shared lane
|
|
231
|
-
|
|
232
206
|
## Conditional Examples
|
|
233
207
|
|
|
234
208
|
Use these only when the campaign motion matches.
|
|
@@ -445,25 +419,24 @@ Tokenized shape:
|
|
|
445
419
|
Hey there
|
|
446
420
|
|
|
447
421
|
Thanks for the support on my post about {{post_topic_line}}
|
|
422
|
+
|
|
423
|
+
Curious, is this something you're dealing with right now?
|
|
448
424
|
```
|
|
449
425
|
|
|
450
426
|
Transfer rules:
|
|
451
427
|
|
|
452
428
|
- Do not hardcode a post topic. Fill `{{post_topic_line}}` from the actual
|
|
453
429
|
selected sender-authored post or use a safer source-specific phrase.
|
|
454
|
-
- Do not add a relevance question, CTA, product line, or extra paragraph after
|
|
455
|
-
the thank-you line. The reply-rate lesson is the casual warm acknowledgment,
|
|
456
|
-
not a cold pitch appended to it.
|
|
457
430
|
- Do not use "showing some love"; it reads too casual for executive/VP/director
|
|
458
431
|
contacts.
|
|
459
432
|
- Keep the thank-you line. The point of this motion is acknowledging real
|
|
460
|
-
sender-owned support
|
|
461
|
-
|
|
462
|
-
|
|
433
|
+
sender-owned support before asking a light relevance question. Do not flatten
|
|
434
|
+
it back to "saw you pop up" unless the sender specifically asks for that
|
|
435
|
+
wording.
|
|
463
436
|
- Do not copy this shape into shared lanes, third-party thread sources, or cold
|
|
464
437
|
fallback campaigns.
|
|
465
|
-
-
|
|
466
|
-
message one.
|
|
438
|
+
- Keep the question closed and problem-oriented; do not pitch, ask for a
|
|
439
|
+
meeting, or mention internal product vocabulary in message one.
|
|
467
440
|
|
|
468
441
|
## Useful CTA Shapes
|
|
469
442
|
|
|
@@ -199,21 +199,11 @@ Shape:
|
|
|
199
199
|
Prefer:
|
|
200
200
|
|
|
201
201
|
- lowercase or casual casing when the motion supports it
|
|
202
|
-
- an honest line like "hope this is relevant" or "so may be off, but this seemed relevant"
|
|
202
|
+
- an honest line like "hope this is relevant" or "so may be off, but this seemed relevant"
|
|
203
203
|
- a concrete CTA that names the useful conversation or asset
|
|
204
204
|
- binary options only when the brief explicitly supports two real next steps
|
|
205
205
|
- direct wording over polished marketing language
|
|
206
206
|
|
|
207
|
-
Shared Evergreen Signal Discovery override:
|
|
208
|
-
|
|
209
|
-
- Do not use `hope this is relevant`, `might be interested`, or `saw you in a
|
|
210
|
-
few conversations` as the opener.
|
|
211
|
-
- Do not use `I'm building`, `I've mapped`, `my company`, or `my team`.
|
|
212
|
-
- Turn the source topic into a buyer problem bridge, then write the product line
|
|
213
|
-
in team/company voice.
|
|
214
|
-
- If the source topic cannot support a concrete bridge, omit the source line and
|
|
215
|
-
start from a role/company/problem observation.
|
|
216
|
-
|
|
217
207
|
Avoid:
|
|
218
208
|
|
|
219
209
|
- copying a gimmick like "from a claude code terminal" unless the brief
|
|
@@ -74,7 +74,7 @@ Revise or reject the sample when any of these happen.
|
|
|
74
74
|
- **actions are implied, not stated** — e.g. "runs that chain as AI agents" when a clearer version would name the specific actions (verb + object, one per line)
|
|
75
75
|
- **category-level opener used when a per-lead signal exists** — if `lead-sample.json` carries any per-lead signal (post, hire, visible tool, topic engagement), the opener must reference it. Category-level openers of shape `"Most [category] teams still do X by hand"` are only acceptable when zero per-lead signal is in the sample. When a category-level opener is used as fallback, Findings must flag it explicitly
|
|
76
76
|
- **mind-reading from engagement signals** — a topic engagement, post, public activity, role, company, or hiring trigger does not prove buyer intent. Reject phrases like `"AI-GTM stack is clearly on your mind"`, `"you're clearly focused on..."`, `"obviously relevant"`, or `"already thinking about..."` unless that exact priority is explicitly present in `lead-sample.json`. Translate to low-certainty buyer context or omit the signal from copy.
|
|
77
|
-
- **source-y signal narration** — reject `"saw you on..."`, `"saw you engaging with..."`, `"you commented on..."`, `"your LinkedIn activity..."`, `"you might not remember the thread..."`, `"found you through [source] and your role looked close..."`, or any line that makes the recipient feel watched unless the chosen archived motion is intentionally self-aware about the signal. For sender-owned LinkedIn post sources, a light first-person acknowledgment is allowed when row data proves a reaction/comment: `"appreciate you showing some love on my post about [topic]"` or `"thanks for showing support on my [topic] post"`. Do not name a comment unless comment text is present. Follow the acknowledgment with a soft relevance bridge before broad pain/product copy, e.g. `"figured this might be relevant if LinkedIn is becoming more of a GTM channel for [company]"`. For third-party LinkedIn-post-sourced campaigns, a topic-level bridge is allowed when it explains why the note exists and stays apologetically uncertain: `"saw you in a few conversations about [topic], so may be off, but this seemed relevant."`, `"saw you in a few conversations around [topic], so hope this is relevant."`, or `"found you in a thread about [topic], so may be off, but this seemed relevant."`
|
|
77
|
+
- **source-y signal narration** — reject `"saw you on..."`, `"saw you engaging with..."`, `"you commented on..."`, `"your LinkedIn activity..."`, `"you might not remember the thread..."`, `"found you through [source] and your role looked close..."`, or any line that makes the recipient feel watched unless the chosen archived motion is intentionally self-aware about the signal. For sender-owned LinkedIn post sources, a light first-person acknowledgment is allowed when row data proves a reaction/comment: `"appreciate you showing some love on my post about [topic]"` or `"thanks for showing support on my [topic] post"`. Do not name a comment unless comment text is present. Follow the acknowledgment with a soft relevance bridge before broad pain/product copy, e.g. `"figured this might be relevant if LinkedIn is becoming more of a GTM channel for [company]"`. For third-party LinkedIn-post-sourced campaigns, a topic-level bridge is allowed when it explains why the note exists and stays apologetically uncertain: `"saw you in a few conversations about [topic], so may be off, but this seemed relevant."`, `"saw you in a few conversations around [topic], so hope this is relevant."`, or `"found you in a thread about [topic], so may be off, but this seemed relevant."` Reserve `"raise your hand"` language for explicit lead-magnet comments, replies, or opt-ins. Translate the signal into natural buyer context or omit it.
|
|
78
78
|
- **sender-owned source rendered as third-party** — hard fail when the source post is sender-owned and the draft says `"found you in a thread"`, `"saw you in a thread"`, `"saw you in conversations"`, or `"saw you in a few conversations"` as if the post were third-party. For sender-owned sources, the copy must either use a light first-person acknowledgment plus a soft relevance bridge, or omit the source line. If multiple senders may send the campaign, or the final sender/source-owner match is ambiguous, do not assume a specific `my post` voice or name a specific sender; omit the sender-owned source line until row-level sender ownership is proven. Neutral low-certainty thread/source language is only for truly third-party sources.
|
|
79
79
|
- **fake line-to-line continuity** — reject line stacks where the source acknowledgment, relevance bridge, product line, and CTA do not actually build on each other. Each line must make the next line feel earned. If two adjacent lines could be swapped, deleted, or joined with `"anyway"` without changing the meaning, the transition is fake. In sender-owned post campaigns, the chain should be: support on my post -> why this topic may matter for the company -> what the product/problem does about that same topic -> low-friction next step.
|
|
80
80
|
- **assumptive title-fit opener** — reject `"Your [role] role at [company] looked close to this problem"` or `"looked close to this outbound campaign problem"`. This asserts fit from title/company. Keep the apologetic uncertainty instead: `"may be off, but if [workflow] is relevant to what you're working on..."`.
|