@sellable/install 0.1.68 → 0.1.69

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.
@@ -1089,6 +1089,112 @@ function codexPluginSkills() {
1089
1089
  ];
1090
1090
  }
1091
1091
 
1092
+ function codexCustomAgents() {
1093
+ const commonPrefix = `You are part of the Sellable find-leads source scout.
1094
+
1095
+ Work only on your assigned source lane. Do not import leads, create campaigns, write campaign artifacts, or draft messages. Return concise structured evidence to the parent agent so it can synthesize the lead-source decision.`;
1096
+
1097
+ return [
1098
+ {
1099
+ filename: "linkedin-engagement-scout.toml",
1100
+ content: `name = "linkedin_engagement_scout"
1101
+ description = "Sellable lead-source scout for LinkedIn post engagement and active conversation signals."
1102
+ model_reasoning_effort = "medium"
1103
+ sandbox_mode = "read-only"
1104
+ nickname_candidates = ["LinkedIn Engagement Scout", "Post Scout", "Engager Scout"]
1105
+ developer_instructions = """
1106
+ ${commonPrefix}
1107
+
1108
+ Your lane is LinkedIn Engagement. Internally, use the signal-discovery provider prompt and the Signals tools when available.
1109
+
1110
+ Use the inherited Sellable MCP tools when available:
1111
+ - get_provider_prompt for signal-discovery before searching
1112
+ - search_signals to find recent post lanes
1113
+ - fetch_post_engagers to sample engagers from selected posts
1114
+
1115
+ Process:
1116
+ 1. Search 3-5 keyword/topic lanes, favoring fresh posts from the last 7-14 days.
1117
+ 2. Select 3-5 promising posts when available.
1118
+ 3. Fetch or sample engagers for selected posts and score rough ICP fit from visible headline/display cues.
1119
+ 4. Estimate usable prospects per selected post from sampled pass rate.
1120
+ 5. If the sample is good but volume is low, say how many more similar posts should be added or scraped.
1121
+
1122
+ Return:
1123
+ - keyword lanes searched, timeframe, raw posts found, finalist posts reviewed
1124
+ - selected post URL/title, author/topic, age, engager count, sampled engagers, good fits as n/N, estimated usable prospects per post, use/discard
1125
+ - sample leads if any
1126
+ - estimated good-fit range, directional reply-rate range, false positives, recommendation, confidence
1127
+ """
1128
+ `,
1129
+ },
1130
+ {
1131
+ filename: "sales-nav-scout.toml",
1132
+ content: `name = "sales_nav_scout"
1133
+ description = "Sellable lead-source scout for Sales Navigator role, company, and activity filters."
1134
+ model_reasoning_effort = "medium"
1135
+ sandbox_mode = "read-only"
1136
+ nickname_candidates = ["Sales Nav Scout", "Role Filter Scout", "Activity Scout"]
1137
+ developer_instructions = """
1138
+ ${commonPrefix}
1139
+
1140
+ Your lane is Sales Nav.
1141
+
1142
+ Use the inherited Sellable MCP tools when available:
1143
+ - get_provider_prompt for sales-nav before searching
1144
+ - lookup_sales_nav_filter before dynamic filters
1145
+ - search_sales_nav for campaignless preview searches
1146
+
1147
+ Process:
1148
+ 1. Preserve target role names with CURRENT_TITLE lookups; do not rely on seniority alone when the brief names concrete roles.
1149
+ 2. Build a broad-but-reasonable baseline from role/title, geography, company size, industry/account context, and recent LinkedIn activity when relevant.
1150
+ 3. Run the baseline plus 1-2 refinements if the first pass is noisy or under-scaled.
1151
+ 4. Verify filters actually applied: search URL contains filters, first-page rows match the intended lane, and result count does not look like an unfiltered pool.
1152
+
1153
+ Return:
1154
+ - exact filter recipe and lookup IDs used
1155
+ - raw result count, sampled people, good fits as n/N
1156
+ - estimated good-fit range after cleanup
1157
+ - directional acceptance/reply-rate ranges
1158
+ - sample leads, false positives, recommendation, confidence
1159
+ """
1160
+ `,
1161
+ },
1162
+ {
1163
+ filename: "prospeo-contact-scout.toml",
1164
+ content: `name = "prospeo_contact_scout"
1165
+ description = "Sellable lead-source scout for Prospeo account/domain and broad contact expansion."
1166
+ model_reasoning_effort = "medium"
1167
+ sandbox_mode = "read-only"
1168
+ nickname_candidates = ["Prospeo Contact Scout", "Domain Scout", "Contact Scout"]
1169
+ developer_instructions = """
1170
+ ${commonPrefix}
1171
+
1172
+ Your lane is Prospeo Contact.
1173
+
1174
+ Use the inherited Sellable MCP tools when available:
1175
+ - get_provider_prompt for prospeo before searching
1176
+ - load_csv_domains when the parent supplies a CSV or domain list and no domainFilterId exists
1177
+ - search_prospeo for campaignless people previews
1178
+
1179
+ Process:
1180
+ 1. Identify whether this is domain/account targeting or broad persona expansion.
1181
+ 2. For domain targeting, use or create the standalone domainFilterId before searching; never pass raw domains directly into search_prospeo.
1182
+ 3. Run the narrowest useful Prospeo people preview and 1-2 refinements if quality or scale is unclear.
1183
+ 4. Call out that Prospeo gives contact/account coverage but usually weaker LinkedIn intent than LinkedIn Engagement or Sales Nav activity slices.
1184
+
1185
+ Return:
1186
+ - mode and domain/account inputs
1187
+ - exact search recipe
1188
+ - raw result count, sampled people, good fits as n/N
1189
+ - estimated good-fit range after cleanup
1190
+ - directional reply-rate range
1191
+ - sample leads, false positives, recommendation, confidence
1192
+ """
1193
+ `,
1194
+ },
1195
+ ];
1196
+ }
1197
+
1092
1198
  function writeCodexPluginSkills(pluginRoot, opts) {
1093
1199
  for (const skill of codexPluginSkills()) {
1094
1200
  const skillRoot = join(pluginRoot, "skills", skill.dir);
@@ -1104,6 +1210,12 @@ function writeCodexPluginSkills(pluginRoot, opts) {
1104
1210
  }
1105
1211
  }
1106
1212
 
1213
+ function writeCodexCustomAgents(home, opts) {
1214
+ for (const agent of codexCustomAgents()) {
1215
+ writeFile(join(home, "agents", agent.filename), agent.content, opts);
1216
+ }
1217
+ }
1218
+
1107
1219
  function installCodexDesktopPlugin(opts) {
1108
1220
  const home = codexHome();
1109
1221
  const configPath = join(home, "config.toml");
@@ -1154,6 +1266,7 @@ function installCodexDesktopPlugin(opts) {
1154
1266
  opts
1155
1267
  );
1156
1268
  writeCodexPluginSkills(pluginRoot, opts);
1269
+ writeCodexCustomAgents(home, opts);
1157
1270
 
1158
1271
  if (!opts.dryRun) {
1159
1272
  rmSync(cacheRoot, { recursive: true, force: true });
@@ -1220,6 +1333,7 @@ enabled = false`
1220
1333
  logVerbose(
1221
1334
  `${C.grey}+ enable [features].default_mode_request_user_input in ${configPath}${C.reset}`
1222
1335
  );
1336
+ logVerbose(`${C.grey}+ write Codex custom scout agents in ${home}/agents${C.reset}`);
1223
1337
  }
1224
1338
 
1225
1339
  return {
@@ -1442,6 +1556,18 @@ function verify(opts) {
1442
1556
  ? "Codex skill bundle present"
1443
1557
  : "Codex skill bundle missing",
1444
1558
  });
1559
+ const codexAgentPaths = codexCustomAgents().map((agent) =>
1560
+ join(codexHome(), "agents", agent.filename)
1561
+ );
1562
+ const hasCodexScouts = codexAgentPaths.every((agentPath) =>
1563
+ existsSync(agentPath)
1564
+ );
1565
+ checks.push({
1566
+ ok: hasCodexScouts,
1567
+ label: hasCodexScouts
1568
+ ? "Codex custom scout agents present"
1569
+ : "Codex custom scout agents missing",
1570
+ });
1445
1571
  const configPath = join(codexHome(), "config.toml");
1446
1572
  const configContent = existsSync(configPath)
1447
1573
  ? readFileSync(configPath, "utf8")
@@ -1592,6 +1718,7 @@ function printNextSteps(installedHosts, authReused) {
1592
1718
  if (hasCodex) {
1593
1719
  console.log(` ${C.green}✓${C.reset} Codex Desktop plugin installed`);
1594
1720
  console.log(` ${C.green}✓${C.reset} Skills installed`);
1721
+ console.log(` ${C.green}✓${C.reset} Codex source scout agents installed`);
1595
1722
  }
1596
1723
  if (authReused) {
1597
1724
  console.log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.68",
3
+ "version": "0.1.69",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {
@@ -99,10 +99,15 @@ denominator, and sample basis.
99
99
 
100
100
  When the user has not supplied a source and multiple source angles are viable,
101
101
  scout those angles as independent branches when the host can actually do it:
102
- Signals / active LinkedIn posts, Sales Nav / title + company filters, and
103
- Prospeo / domains only when relevant. If the host runs them sequentially, do not
104
- claim they ran in parallel. In chat, call the downstream copy stage `message
105
- generation`; `message-validation.md` is only an internal proof artifact.
102
+ LinkedIn Engagement / active post engagers (internal `signal-discovery`
103
+ provider prompt), Sales Nav / title + company filters, and Prospeo Contact /
104
+ domains only when relevant. In Codex, explicitly spawn the named custom scouts
105
+ `linkedin_engagement_scout`, `sales_nav_scout`, and `prospeo_contact_scout` for
106
+ the credible lanes; Codex does not infer subagent fan-out from generic source
107
+ comparison wording. In Claude Code, keep using the existing Task agents under
108
+ `.claude/agents/`. If the host runs them sequentially, do not claim they ran in
109
+ parallel. In chat, call the downstream copy stage `message generation`;
110
+ `message-validation.md` is only an internal proof artifact.
106
111
 
107
112
  Use rendered Markdown for user review surfaces, not fenced code blocks. Keep
108
113
  lines short, use indexed section labels and bullets, and translate internal