@sellable/mcp 0.1.416 → 0.1.418

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 CHANGED
@@ -66,9 +66,8 @@ 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"`, optional through-date selectors such as
70
- `--until 2026-06-30`, or typed MCP calls to `refill_sends({ yolo, senders,
71
- untilDate })` from:
69
+ `--sender "Christian Reyes"` or typed MCP calls to `refill_sends({ yolo,
70
+ senders })` from:
72
71
 
73
72
  - `mcp/sellable/skills/refill-sends/SKILL.md`
74
73
 
@@ -297,7 +296,6 @@ Use the refill command for sender send refills:
297
296
 
298
297
  ```
299
298
  /sellable:refill-sends --yolo
300
- /sellable:refill-sends --yolo --until 2026-06-30
301
299
  /sellable:refill-sends --sender "Christian Reyes" --sender "Thomas Nobbs"
302
300
  ```
303
301
 
@@ -325,7 +323,6 @@ Use the Codex refill command with the same flags:
325
323
 
326
324
  ```
327
325
  $sellable:refill-sends --yolo
328
- $sellable:refill-sends --yolo --until 2026-06-30
329
326
  $sellable:refill-sends --sender "Christian Reyes" --sender "Thomas Nobbs"
330
327
  ```
331
328
 
package/dist/index-dev.js CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
@@ -6,7 +6,6 @@ type RefillSendsCommandInput = {
6
6
  senderIds?: string[];
7
7
  senderNames?: string[];
8
8
  horizonSendDays?: number;
9
- untilDate?: string;
10
9
  campaignId?: string;
11
10
  tableId?: string;
12
11
  intent?: RefillSendsIntent;
@@ -49,11 +48,6 @@ export declare const refillSendsToolDefinitions: {
49
48
  maximum: number;
50
49
  description: string;
51
50
  };
52
- untilDate: {
53
- type: string;
54
- pattern: string;
55
- description: string;
56
- };
57
51
  campaignId: {
58
52
  type: string;
59
53
  description: string;
@@ -84,19 +78,7 @@ export declare function refillSendsCommand(input?: RefillSendsCommandInput): {
84
78
  workflowPromptName: string;
85
79
  yolo: boolean;
86
80
  intent: RefillSendsIntent;
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
- };
81
+ horizonSendDays: number;
100
82
  approvalMode: RefillSendsApprovalMode;
101
83
  campaignId: string | null;
102
84
  tableId: string | null;
@@ -119,8 +101,7 @@ export declare function refillSendsCommand(input?: RefillSendsCommandInput): {
119
101
  senders: string[];
120
102
  senderIds: string[];
121
103
  senderNames: string[];
122
- untilDate: string | null;
123
- horizonSendDays: number | null;
104
+ horizonSendDays: number;
124
105
  campaignId: string | undefined;
125
106
  tableId: string | undefined;
126
107
  intent: RefillSendsIntent;
@@ -14,25 +14,6 @@ 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
- }
36
17
  export const refillSendsToolDefinitions = [
37
18
  {
38
19
  name: "refill_sends",
@@ -63,12 +44,7 @@ export const refillSendsToolDefinitions = [
63
44
  type: "number",
64
45
  minimum: 1,
65
46
  maximum: 7,
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.",
47
+ description: "Number of sender-local send days to fill. Defaults to 2.",
72
48
  },
73
49
  campaignId: {
74
50
  type: "string",
@@ -100,10 +76,7 @@ export function refillSendsCommand(input = {}) {
100
76
  const senderNames = normalizeStrings(input.senderNames);
101
77
  const hasSenderSelectors = senders.length > 0 || senderIds.length > 0 || senderNames.length > 0;
102
78
  const yolo = input.yolo === true;
103
- const untilDate = normalizeUntilDate(input.untilDate);
104
- const horizonSendDays = untilDate
105
- ? null
106
- : normalizeHorizonSendDays(input.horizonSendDays);
79
+ const horizonSendDays = normalizeHorizonSendDays(input.horizonSendDays);
107
80
  const intent = input.intent ?? "plain";
108
81
  const approvalMode = input.approvalMode ?? (yolo ? "approve" : "mark_ready");
109
82
  const senderScope = hasSenderSelectors
@@ -118,19 +91,7 @@ export function refillSendsCommand(input = {}) {
118
91
  workflowPromptName: "refill-sends-workflow",
119
92
  yolo,
120
93
  intent,
121
- untilDate,
122
94
  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
- },
134
95
  approvalMode,
135
96
  campaignId: input.campaignId ?? null,
136
97
  tableId: input.tableId ?? null,
@@ -144,9 +105,6 @@ export function refillSendsCommand(input = {}) {
144
105
  'Load get_subskill_prompt({ subskillName: "refill-sends-workflow" }) before any product operation.',
145
106
  `Resolve route with resolve_campaign_fill_route({ intent: "${intent}"${input.campaignId ? `, campaignId: "${input.campaignId}"` : ""}${input.tableId ? `, tableId: "${input.tableId}"` : ""} }).`,
146
107
  "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.`,
150
108
  "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.",
151
109
  "Fresh reread get_campaign_refill_state immediately before any import, prep, approval, or horizon-fill mutation.",
152
110
  ],
@@ -167,12 +125,10 @@ export function refillSendsCommand(input = {}) {
167
125
  hostExamples: {
168
126
  claude: [
169
127
  "/sellable:refill-sends --yolo",
170
- "/sellable:refill-sends --yolo --until 2026-06-30",
171
128
  '/sellable:refill-sends --sender "Christian Reyes" --sender "Thomas Nobbs"',
172
129
  ],
173
130
  codex: [
174
131
  "$sellable:refill-sends --yolo",
175
- "$sellable:refill-sends --yolo --until 2026-06-30",
176
132
  '$sellable:refill-sends --sender "Christian Reyes" --sender "Thomas Nobbs"',
177
133
  ],
178
134
  mcpTool: {
@@ -182,7 +138,6 @@ export function refillSendsCommand(input = {}) {
182
138
  senders,
183
139
  senderIds,
184
140
  senderNames,
185
- untilDate,
186
141
  horizonSendDays,
187
142
  campaignId: input.campaignId,
188
143
  tableId: input.tableId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.416",
3
+ "version": "0.1.418",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -55,6 +55,12 @@ 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
+
58
64
  **Severity:** REJECT
59
65
 
60
66
  ## Tell #3 — Explicit date or duration references about the recipient
@@ -203,6 +203,32 @@ 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
+
206
232
  ## Conditional Examples
207
233
 
208
234
  Use these only when the campaign motion matches.
@@ -419,24 +445,25 @@ Tokenized shape:
419
445
  Hey there
420
446
 
421
447
  Thanks for the support on my post about {{post_topic_line}}
422
-
423
- Curious, is this something you're dealing with right now?
424
448
  ```
425
449
 
426
450
  Transfer rules:
427
451
 
428
452
  - Do not hardcode a post topic. Fill `{{post_topic_line}}` from the actual
429
453
  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.
430
457
  - Do not use "showing some love"; it reads too casual for executive/VP/director
431
458
  contacts.
432
459
  - Keep the thank-you line. The point of this motion is acknowledging real
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.
460
+ sender-owned support without appending a cold relevance question. Do not
461
+ flatten it back to "saw you pop up" unless the sender specifically asks for
462
+ that wording.
436
463
  - Do not copy this shape into shared lanes, third-party thread sources, or cold
437
464
  fallback campaigns.
438
- - Keep the question closed and problem-oriented; do not pitch, ask for a
439
- meeting, or mention internal product vocabulary in message one.
465
+ - Do not pitch, ask for a meeting, or mention internal product vocabulary in
466
+ message one.
440
467
 
441
468
  ## Useful CTA Shapes
442
469
 
@@ -199,11 +199,21 @@ 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" only for non-evergreen weak-signal campaigns
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
+
207
217
  Avoid:
208
218
 
209
219
  - 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."` Reserve `"raise your hand"` language for explicit lead-magnet comments, replies, or opt-ins. Translate the signal into natural buyer context or omit it.
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."` Shared evergreen signal exception: these hedges are blocked for Shared Signal Discovery and other shared evergreen lanes; use a concrete signal/problem bridge or omit the source line. 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..."`.