@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 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"` or typed MCP calls to `refill_sends({ yolo,
70
- senders })` from:
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
- horizonSendDays: number;
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
- horizonSendDays: number;
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 horizonSendDays = normalizeHorizonSendDays(input.horizonSendDays);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.415",
3
+ "version": "0.1.416",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -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 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.
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
- - Do not pitch, ask for a meeting, or mention internal product vocabulary in
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" only for non-evergreen weak-signal campaigns
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."` 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.
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..."`.