@sellable/install 0.1.92 → 0.1.94

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
@@ -6,6 +6,16 @@ Installs Sellable MCP for Claude Code and Codex.
6
6
  npx -y @sellable/install@latest --host all
7
7
  ```
8
8
 
9
+ After install, `sellable create` is a terminal helper that prints the correct
10
+ agent command for launching a campaign:
11
+
12
+ ```bash
13
+ sellable create
14
+ ```
15
+
16
+ Campaign creation itself runs inside Claude Code or Codex, where the Sellable
17
+ MCP tools and approval flow are available.
18
+
9
19
  If you do not pass a token, the installer prompts you for one and shows where to get it.
10
20
 
11
21
  The installer uses package stdio MCP by default:
@@ -26,15 +26,38 @@ Use the inherited Sellable MCP tools when available:
26
26
  Process:
27
27
 
28
28
  1. Read the campaign brief, kickoff doc, or lane prompt supplied by the parent.
29
- 2. Search 3-5 keyword/topic lanes, favoring fresh posts from the last 7-14 days.
30
- 3. Select 3-5 promising posts when available. If a `campaignOfferId` was
31
- supplied, call `select_promising_posts({ campaignOfferId, selectionMode:
32
- "replace", selections, headlineICPCriteria, currentStep:
33
- "signal-discovery" })` before sampling so the watched Signal Discovery table
34
- shows the promoted posts.
35
- 4. Fetch or sample engagers for promoted posts and score rough ICP fit from visible headline/display-name cues only. Do not enrich people during viability estimation.
36
- 5. Estimate usable prospects per selected post from sampled pass rate. If the sample is good but volume is low, say how many more similar posts should be added or scraped.
37
- 6. Return false positives and dead ends explicitly.
29
+ 2. Generate 3-5 intersection keyword/topic lanes, favoring fresh posts from the
30
+ last 7-14 days. Each lane should combine the campaign anchor with the buyer
31
+ pain, use case, or ICP role so fit is high before sampling. For example, for
32
+ a Claude + GTM/outbound campaign, prefer `Claude outbound`, `Claude Code
33
+ LinkedIn outreach`, `AI SDR Claude`, `GTM automation Claude`, and `founder-led
34
+ sales Claude`; do not treat broad anchor-only lanes like `Claude Code`, `MCP`,
35
+ `AI agents`, or `agentic coding` as selectable unless they also include the
36
+ GTM/outbound wedge or the narrower lanes fail.
37
+ 3. Inspect finalist posts by content type before final selection. Prefer posts
38
+ where the topic matches the campaign wedge, not generic high-engagement news.
39
+ 4. If Round 1 produced broad anchor-only inventory, retarget immediately around
40
+ the wedge before sampling. Do not promote or sample broad posts when there are
41
+ narrower posts about the actual campaign pain/use case.
42
+ 5. Promote the first narrow sample set when campaign-attached. If a
43
+ `campaignOfferId` was supplied, call `select_promising_posts({
44
+ campaignOfferId, selectionMode: "replace", selections, headlineICPCriteria,
45
+ currentStep: "signal-discovery" })` before sampling so the watched Signal
46
+ Discovery table shows the promoted posts and the exact posts being tested.
47
+ 6. Fetch or sample engagers for promoted posts and score rough ICP fit from
48
+ visible headline/display-name cues only. Do not enrich people during
49
+ viability estimation.
50
+ 7. Compute capacity before recommending the source: source target good-fit
51
+ leads (default 300 unless the parent supplies a target), reachable engagers,
52
+ sampled ICP-fit rate as `n/N` plus an easy percentage/range, expected usable
53
+ leads per 100 engagers before and after a conservative dedupe/cleanup
54
+ factor, required engagers to scrape (`source target / fit rate`), average
55
+ reachable engagers per right-content post, expected usable leads per
56
+ right-content post after dedupe/cleanup, and posts needed to hit the target.
57
+ 8. Select/promote enough right-content posts to plausibly hit the target. If the
58
+ warm Signals pool is useful but too small, return the expected warm range and
59
+ recommend Sales Nav/Prospeo for scale instead of padding with noisy posts.
60
+ 9. Return false positives and dead ends explicitly.
38
61
 
39
62
  Return a concise structured result with:
40
63
 
@@ -43,8 +66,14 @@ Return a concise structured result with:
43
66
  - `keyword_lanes` with timeframe, raw posts found, finalist posts reviewed
44
67
  - `selected_posts` with URL/title, author/topic, age, engager count, sampled engagers, good fits as n/N, estimated usable prospects per post, use/discard
45
68
  - `sample_leads`, if any
69
+ - `approval_math` with eligible posts, source target good-fit leads, sampled
70
+ engagers, ICP-fit rate as `n/N` plus percentage/range, good-fit prospects per
71
+ 100 engagers, required engagers to scrape, average reachable engagers per
72
+ post, expected usable prospects per post after cleanup, posts needed for
73
+ target, selected post count, review-batch import limit, expected usable lead
74
+ range, and scale fallback
46
75
  - `estimated_good_fit_range`
47
- - `expected_reply_rate_range`, directional if inferred
76
+ - `message_context_strength`, directional and source-specific
48
77
  - `false_positive_patterns`
49
78
  - `recommendation`
50
79
  - `confidence`
@@ -53,5 +82,16 @@ Evidence standards:
53
82
 
54
83
  - Do not trust raw post volume without inspecting finalist post quality.
55
84
  - Prefer sample-based pass rates over intuition.
85
+ - Prefer narrow intersection topics over broad audience topics. A post about
86
+ the anchor technology alone is not enough; the post should also express the
87
+ GTM/outbound/buyer pain, workflow, or role context that makes the campaign
88
+ relevant.
89
+ - Do not make the user infer capacity. Say, plainly, how many eligible posts
90
+ exist, how many sampled engagers looked in-ICP, how many good-fit prospects
91
+ that implies per 100 engagers, how many usable prospects one right-content
92
+ post should yield after cleanup, how many engagers must be scraped for the
93
+ 300-good-fit source target, how many posts are needed for that source target,
94
+ and which posts you would use. Also say the first campaign import remains the
95
+ bounded 15-row review batch.
56
96
  - If `fetch_post_engagers` is unavailable or fails, report that explicitly and mark the estimate lower-confidence.
57
97
  - Keep LinkedIn Engagement viable when selected posts can produce roughly 150+ ICP-fit warm prospects before final filtering, even if Sales Nav is more scalable.
@@ -26,8 +26,16 @@ Process:
26
26
  2. Preserve target role names with `CURRENT_TITLE` lookups; do not rely on seniority alone when the brief names concrete roles.
27
27
  3. When `lookup_sales_nav_filter` returns multiple title options, choose the closest semantic title match instead of the first result.
28
28
  4. Build a broad-but-reasonable baseline from role/title, geography, company size, industry/account context, and recent LinkedIn activity when relevant.
29
- 5. Run the baseline plus 1-2 refinements if the first pass is noisy or under-scaled.
30
- 6. Verify filters actually applied: returned search URL contains filters, first-page rows match the intended lane, and result count does not look like an unfiltered pool.
29
+ 5. Check scale against the source target good-fit lead count (default 300
30
+ unless the parent supplies a target). If raw preview volume or projected usable volume
31
+ is below target, do not present the tiny result as the scale fallback yet.
32
+ Loosen nonessential filters in order: remove recent-activity first, widen
33
+ adjacent title variants, widen geography/company-size constraints, and only
34
+ keep hard ICP requirements from the brief.
35
+ 6. Run the baseline plus 1-2 refinements or loosening passes if the first pass
36
+ is noisy or under-scaled. Label the final pool as constrained if it still
37
+ cannot plausibly reach the target after loosening.
38
+ 7. Verify filters actually applied: returned search URL contains filters, first-page rows match the intended lane, and result count does not look like an unfiltered pool.
31
39
 
32
40
  Return a concise structured result with:
33
41
 
@@ -36,6 +44,11 @@ Return a concise structured result with:
36
44
  - `exact_filter_recipe`
37
45
  - `lookup_ids_used`
38
46
  - `raw_result_count`
47
+ - `scale_check` with source target good-fit lead count, preview/raw volume, sampled
48
+ good fits as n/N, projected usable count, and whether the pool can reach the
49
+ target
50
+ - `loosening_attempts` with what was removed or widened when the pool was too
51
+ tight
39
52
  - `sampled_people` and good fits as n/N
40
53
  - `estimated_good_fit_range_after_cleanup`
41
54
  - `expected_acceptance_rate_range`, directional if inferred
@@ -49,5 +62,9 @@ Evidence standards:
49
62
 
50
63
  - Optimize for a useful prospect pool, not max volume at any cost.
51
64
  - Bias toward `POSTED_ON_LINKEDIN` for reply-likelihood when the pool still has enough scale.
65
+ - Do not over-tighten fallback filters into a pool that cannot be meaningfully
66
+ larger than the warm-post path. If Sales Nav is offered for scale, it should
67
+ either project to the target good-fit count or clearly say it is too tight and
68
+ name the next broadening/Prospeo option.
52
69
  - Do not hand-wave missing filter IDs.
53
70
  - If Sales Nav returns a giant unfiltered pool, discard that result and retry with valid filters before recommending it.
@@ -129,9 +129,15 @@ function usage() {
129
129
  return `Sellable agent installer
130
130
 
131
131
  Usage:
132
+ sellable create
132
133
  sellable-install [options]
133
134
  npx -y @sellable/install@latest -- [options]
134
135
 
136
+ Commands:
137
+ create Show how to launch the create-campaign workflow.
138
+ auth set <token> Save a Sellable API token for first-run auth.
139
+ uninstall Remove Sellable host config and installed artifacts.
140
+
135
141
  Options:
136
142
  --host <host> claude, codex, or all. Default: all
137
143
  --server <mode> package, local, or hosted. Default: package
@@ -158,6 +164,34 @@ Auth:
158
164
  `;
159
165
  }
160
166
 
167
+ function printCreateCommandHint() {
168
+ printBanner();
169
+ console.log(
170
+ ` ${C.bold}Create campaigns from Claude Code or Codex.${C.reset}`
171
+ );
172
+ console.log("");
173
+ console.log(
174
+ ` ${C.grey}The terminal command installs and verifies Sellable. Campaign creation runs inside your agent session so the workflow can use MCP tools, live state, and approval gates.${C.reset}`
175
+ );
176
+ console.log("");
177
+ console.log(` ${"═".repeat(63)}`);
178
+ console.log(` ${C.bold}Start a Sellable campaign:${C.reset}`);
179
+ console.log(` ${"═".repeat(63)}`);
180
+ console.log("");
181
+ console.log("");
182
+ printAgentBox("Using Claude Code?", "claude", "/sellable:create-campaign");
183
+ console.log("");
184
+ console.log("");
185
+ printAgentBox("Using Codex?", "codex", "$sellable:create-campaign");
186
+ console.log("");
187
+ console.log(` ${"─".repeat(63)}`);
188
+ console.log(` ${C.grey}If those commands are missing, run:${C.reset}`);
189
+ console.log("");
190
+ console.log(` ${C.cyan}sellable --host all${C.reset}`);
191
+ console.log(` ${C.cyan}sellable --verify-only --host all${C.reset}`);
192
+ console.log("");
193
+ }
194
+
161
195
  function parseArgs(argv) {
162
196
  const opts = {
163
197
  host: process.env.SELLABLE_INSTALL_HOST || "all",
@@ -2245,6 +2279,10 @@ async function main() {
2245
2279
  runUninstall();
2246
2280
  process.exit(0);
2247
2281
  }
2282
+ if (rawArgs[0] === "create") {
2283
+ printCreateCommandHint();
2284
+ process.exit(0);
2285
+ }
2248
2286
  const opts = parseArgs(process.argv.slice(2));
2249
2287
  if (opts.help) {
2250
2288
  console.log(usage());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.92",
3
+ "version": "0.1.94",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {