@sellable/install 0.1.70 → 0.1.71

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
@@ -35,9 +35,11 @@ Auth is stored once at:
35
35
 
36
36
  Claude Code and Codex are configured to launch the same packaged MCP server. The
37
37
  installer also writes Sellable source-scout agents for both hosts from the
38
- packaged `agents/` registry: Claude Code gets `lead-explorer-*` subagents, and
39
- Codex gets `linkedin_engagement_scout`, `sales_nav_scout`, and
40
- `prospeo_contact_scout`.
38
+ packaged `agents/` registry: Claude Code gets `source-scout-*` subagents, and
39
+ Codex gets `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and
40
+ `source-scout-prospeo-contact`. The MCP exposes the same list through
41
+ `get_source_scout_registry`, so adding a future scout starts in one registry
42
+ entry instead of scattered prompt edits.
41
43
 
42
44
  ## Names
43
45
 
@@ -2,12 +2,27 @@
2
2
  "version": 1,
3
3
  "agents": [
4
4
  {
5
- "id": "linkedin-engagement-scout",
6
- "promptFile": "linkedin-engagement-scout.md",
5
+ "id": "linkedin-engagement",
6
+ "name": "source-scout-linkedin-engagement",
7
+ "promptFile": "source-scout-linkedin-engagement.md",
7
8
  "displayName": "LinkedIn Engagement Scout",
9
+ "provider": "signal-discovery",
10
+ "lane": "linkedin-engagement",
11
+ "legacy": {
12
+ "codex": [
13
+ {
14
+ "name": "linkedin_engagement_scout",
15
+ "filename": "linkedin-engagement-scout.toml"
16
+ }
17
+ ],
18
+ "claude": [
19
+ {
20
+ "name": "lead-explorer-signals",
21
+ "filename": "lead-explorer-signals.md"
22
+ }
23
+ ]
24
+ },
8
25
  "codex": {
9
- "name": "linkedin_engagement_scout",
10
- "filename": "linkedin-engagement-scout.toml",
11
26
  "description": "Sellable lead-source scout for LinkedIn post engagement and active conversation signals.",
12
27
  "modelReasoningEffort": "medium",
13
28
  "sandboxMode": "read-only",
@@ -18,8 +33,6 @@
18
33
  ]
19
34
  },
20
35
  "claude": {
21
- "name": "lead-explorer-signals",
22
- "filename": "lead-explorer-signals.md",
23
36
  "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs LinkedIn post engagement, Signals, or active conversation evidence.",
24
37
  "model": "inherit",
25
38
  "background": true,
@@ -36,12 +49,27 @@
36
49
  }
37
50
  },
38
51
  {
39
- "id": "sales-nav-scout",
40
- "promptFile": "sales-nav-scout.md",
52
+ "id": "sales-nav",
53
+ "name": "source-scout-sales-nav",
54
+ "promptFile": "source-scout-sales-nav.md",
41
55
  "displayName": "Sales Nav Scout",
56
+ "provider": "sales-nav",
57
+ "lane": "sales-nav",
58
+ "legacy": {
59
+ "codex": [
60
+ {
61
+ "name": "sales_nav_scout",
62
+ "filename": "sales-nav-scout.toml"
63
+ }
64
+ ],
65
+ "claude": [
66
+ {
67
+ "name": "lead-explorer-sales-nav",
68
+ "filename": "lead-explorer-sales-nav.md"
69
+ }
70
+ ]
71
+ },
42
72
  "codex": {
43
- "name": "sales_nav_scout",
44
- "filename": "sales-nav-scout.toml",
45
73
  "description": "Sellable lead-source scout for Sales Navigator role, company, and activity filters.",
46
74
  "modelReasoningEffort": "medium",
47
75
  "sandboxMode": "read-only",
@@ -52,8 +80,6 @@
52
80
  ]
53
81
  },
54
82
  "claude": {
55
- "name": "lead-explorer-sales-nav",
56
- "filename": "lead-explorer-sales-nav.md",
57
83
  "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs Sales Navigator title, company, geography, or activity-filter evidence.",
58
84
  "model": "inherit",
59
85
  "background": true,
@@ -70,12 +96,27 @@
70
96
  }
71
97
  },
72
98
  {
73
- "id": "prospeo-contact-scout",
74
- "promptFile": "prospeo-contact-scout.md",
99
+ "id": "prospeo-contact",
100
+ "name": "source-scout-prospeo-contact",
101
+ "promptFile": "source-scout-prospeo-contact.md",
75
102
  "displayName": "Prospeo Contact Scout",
103
+ "provider": "prospeo",
104
+ "lane": "prospeo-contact",
105
+ "legacy": {
106
+ "codex": [
107
+ {
108
+ "name": "prospeo_contact_scout",
109
+ "filename": "prospeo-contact-scout.toml"
110
+ }
111
+ ],
112
+ "claude": [
113
+ {
114
+ "name": "lead-explorer-prospeo",
115
+ "filename": "lead-explorer-prospeo.md"
116
+ }
117
+ ]
118
+ },
76
119
  "codex": {
77
- "name": "prospeo_contact_scout",
78
- "filename": "prospeo-contact-scout.toml",
79
120
  "description": "Sellable lead-source scout for Prospeo account/domain and broad contact expansion.",
80
121
  "modelReasoningEffort": "medium",
81
122
  "sandboxMode": "read-only",
@@ -86,8 +127,6 @@
86
127
  ]
87
128
  },
88
129
  "claude": {
89
- "name": "lead-explorer-prospeo",
90
- "filename": "lead-explorer-prospeo.md",
91
130
  "description": "Use proactively as a background Sellable source scout when find-leads or create-campaign needs Prospeo account, domain-list, CSV-domain, or verified-contact evidence.",
92
131
  "model": "inherit",
93
132
  "background": true,
@@ -465,6 +465,7 @@ const CREATE_CAMPAIGN_ALLOWED_TOOLS = [
465
465
  "mcp__sellable__get_subskill_asset",
466
466
  "mcp__sellable__search_subskill_prompts",
467
467
  "mcp__sellable__get_provider_prompt",
468
+ "mcp__sellable__get_source_scout_registry",
468
469
  "mcp__sellable__get_message_prompt",
469
470
  "mcp__sellable__get_active_workspace",
470
471
  "mcp__sellable__list_senders",
@@ -1124,14 +1125,18 @@ function loadCanonicalAgents() {
1124
1125
 
1125
1126
  return registry.agents.map((agent) => {
1126
1127
  const promptPath = join(root, agent.promptFile || "");
1127
- if (!agent.id || !agent.promptFile || !existsSync(promptPath)) {
1128
+ if (!agent.id || !agent.name || !agent.promptFile || !existsSync(promptPath)) {
1128
1129
  throw new Error(`Invalid Sellable agent registry entry: ${agent.id || "unknown"}`);
1129
1130
  }
1130
- if (!agent.codex?.name || !agent.codex?.filename) {
1131
- throw new Error(`Sellable agent ${agent.id} is missing Codex metadata.`);
1131
+ if (agent.codex?.name && agent.codex.name !== agent.name) {
1132
+ throw new Error(
1133
+ `Sellable agent ${agent.id} has divergent Codex name ${agent.codex.name}; use canonical name ${agent.name}.`
1134
+ );
1132
1135
  }
1133
- if (!agent.claude?.name || !agent.claude?.filename) {
1134
- throw new Error(`Sellable agent ${agent.id} is missing Claude metadata.`);
1136
+ if (agent.claude?.name && agent.claude.name !== agent.name) {
1137
+ throw new Error(
1138
+ `Sellable agent ${agent.id} has divergent Claude name ${agent.claude.name}; use canonical name ${agent.name}.`
1139
+ );
1135
1140
  }
1136
1141
 
1137
1142
  return {
@@ -1141,6 +1146,22 @@ function loadCanonicalAgents() {
1141
1146
  });
1142
1147
  }
1143
1148
 
1149
+ function codexAgentFilename(agent) {
1150
+ return agent.codex?.filename || `${agent.name}.toml`;
1151
+ }
1152
+
1153
+ function claudeAgentFilename(agent) {
1154
+ return agent.claude?.filename || `${agent.name}.md`;
1155
+ }
1156
+
1157
+ function legacyCodexCustomAgents() {
1158
+ return loadCanonicalAgents().flatMap((agent) => agent.legacy?.codex || []);
1159
+ }
1160
+
1161
+ function legacyClaudeCustomAgents() {
1162
+ return loadCanonicalAgents().flatMap((agent) => agent.legacy?.claude || []);
1163
+ }
1164
+
1144
1165
  function tomlArray(values) {
1145
1166
  return `[${values.map((value) => quoteToml(value)).join(", ")}]`;
1146
1167
  }
@@ -1152,7 +1173,7 @@ function tomlMultilineString(value) {
1152
1173
 
1153
1174
  function generateCodexAgentToml(agent) {
1154
1175
  const codex = agent.codex;
1155
- return `name = ${quoteToml(codex.name)}
1176
+ return `name = ${quoteToml(agent.name)}
1156
1177
  description = ${quoteToml(codex.description)}
1157
1178
  model_reasoning_effort = ${quoteToml(codex.modelReasoningEffort || "medium")}
1158
1179
  sandbox_mode = ${quoteToml(codex.sandboxMode || "read-only")}
@@ -1163,8 +1184,8 @@ developer_instructions = ${tomlMultilineString(agent.prompt)}
1163
1184
 
1164
1185
  function codexCustomAgents() {
1165
1186
  return loadCanonicalAgents().map((agent) => ({
1166
- name: agent.codex.name,
1167
- filename: agent.codex.filename,
1187
+ name: agent.name,
1188
+ filename: codexAgentFilename(agent),
1168
1189
  content: generateCodexAgentToml(agent),
1169
1190
  }));
1170
1191
  }
@@ -1172,7 +1193,7 @@ function codexCustomAgents() {
1172
1193
  function generateClaudeAgentMd(agent) {
1173
1194
  const claude = agent.claude;
1174
1195
  return `---
1175
- name: ${claude.name}
1196
+ name: ${agent.name}
1176
1197
  description: ${JSON.stringify(claude.description)}
1177
1198
  tools: ${(claude.tools || []).join(", ")}
1178
1199
  model: ${claude.model || "inherit"}
@@ -1187,8 +1208,8 @@ ${agent.prompt}
1187
1208
 
1188
1209
  function claudeCustomAgents() {
1189
1210
  return loadCanonicalAgents().map((agent) => ({
1190
- name: agent.claude.name,
1191
- filename: agent.claude.filename,
1211
+ name: agent.name,
1212
+ filename: claudeAgentFilename(agent),
1192
1213
  tools: agent.claude.tools || [],
1193
1214
  content: generateClaudeAgentMd(agent),
1194
1215
  }));
@@ -1213,6 +1234,11 @@ function writeCodexCustomAgents(home, opts) {
1213
1234
  for (const agent of codexCustomAgents()) {
1214
1235
  writeFile(join(home, "agents", agent.filename), agent.content, opts);
1215
1236
  }
1237
+ for (const agent of legacyCodexCustomAgents()) {
1238
+ const legacyPath = join(home, "agents", agent.filename);
1239
+ logVerbose(`${C.grey}Removing legacy Codex scout agent ${legacyPath}${C.reset}`);
1240
+ if (!opts.dryRun) rmSync(legacyPath, { force: true });
1241
+ }
1216
1242
  }
1217
1243
 
1218
1244
  function claudeHome() {
@@ -1224,6 +1250,11 @@ function writeClaudeCustomAgents(opts) {
1224
1250
  for (const agent of claudeCustomAgents()) {
1225
1251
  writeFile(join(home, "agents", agent.filename), agent.content, opts);
1226
1252
  }
1253
+ for (const agent of legacyClaudeCustomAgents()) {
1254
+ const legacyPath = join(home, "agents", agent.filename);
1255
+ logVerbose(`${C.grey}Removing legacy Claude scout agent ${legacyPath}${C.reset}`);
1256
+ if (!opts.dryRun) rmSync(legacyPath, { force: true });
1257
+ }
1227
1258
  }
1228
1259
 
1229
1260
  function installCodexDesktopPlugin(opts) {
@@ -1339,6 +1370,9 @@ enabled = false`
1339
1370
  max_threads = 6
1340
1371
  max_depth = 1`
1341
1372
  );
1373
+ for (const agent of legacyCodexCustomAgents()) {
1374
+ content = removeTomlSection(content, `agents.${agent.name}`);
1375
+ }
1342
1376
  for (const agent of codexCustomAgents()) {
1343
1377
  content = upsertTomlTable(
1344
1378
  content,
@@ -1861,7 +1895,7 @@ function runUninstall() {
1861
1895
  after = removeTomlSection(after, "marketplaces.sellable");
1862
1896
  after = removeTomlSection(after, 'plugins."sellable@sellable"');
1863
1897
  after = removeTomlSection(after, 'plugins."sellable@sellable-local"');
1864
- for (const agent of codexCustomAgents()) {
1898
+ for (const agent of [...codexCustomAgents(), ...legacyCodexCustomAgents()]) {
1865
1899
  after = removeTomlSection(after, `agents.${agent.name}`);
1866
1900
  }
1867
1901
  // Collapse 3+ blank lines that the removals may leave behind.
@@ -1884,7 +1918,7 @@ function runUninstall() {
1884
1918
  skipped.push(`Codex config not found at ${codexConfigPath}`);
1885
1919
  }
1886
1920
 
1887
- for (const agent of codexCustomAgents()) {
1921
+ for (const agent of [...codexCustomAgents(), ...legacyCodexCustomAgents()]) {
1888
1922
  const agentPath = join(codexHome(), "agents", agent.filename);
1889
1923
  if (!existsSync(agentPath)) continue;
1890
1924
  try {
@@ -1911,7 +1945,7 @@ function runUninstall() {
1911
1945
  skipped.push(`Claude Code CLI not found`);
1912
1946
  }
1913
1947
 
1914
- for (const agent of claudeCustomAgents()) {
1948
+ for (const agent of [...claudeCustomAgents(), ...legacyClaudeCustomAgents()]) {
1915
1949
  const agentPath = join(claudeHome(), "agents", agent.filename);
1916
1950
  if (!existsSync(agentPath)) continue;
1917
1951
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.70",
3
+ "version": "0.1.71",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {
@@ -11,6 +11,7 @@ allowed-tools:
11
11
  - mcp__sellable__get_subskill_asset
12
12
  - mcp__sellable__search_subskill_prompts
13
13
  - mcp__sellable__get_provider_prompt
14
+ - mcp__sellable__get_source_scout_registry
14
15
  - mcp__sellable__get_message_prompt
15
16
  - mcp__sellable__get_active_workspace
16
17
  - mcp__sellable__list_senders
@@ -102,12 +103,14 @@ scout those angles as independent branches when the host can actually do it:
102
103
  LinkedIn Engagement / active post engagers (internal `signal-discovery`
103
104
  provider prompt), Sales Nav / title + company filters, and Prospeo Contact /
104
105
  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
+ `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and `source-scout-prospeo-contact` for
106
107
  the credible lanes; Codex does not infer subagent fan-out from generic source
107
- comparison wording. In Claude Code, invoke the generated `lead-explorer-*`
108
+ comparison wording. In Claude Code, invoke the generated `source-scout-*`
108
109
  Task/Agent subagents for all credible lanes in one assistant message; the
109
110
  installer writes them from the same canonical Sellable agent registry with
110
- explicit Sellable MCP tool allowlists. If the host runs them sequentially, do not
111
+ explicit Sellable MCP tool allowlists. The create-campaign-v2 subskill calls
112
+ `get_source_scout_registry` before dispatch so the current registry, not this
113
+ copy, is the runtime source of truth. If the host runs them sequentially, do not
111
114
  claim they ran in parallel. In chat, call the downstream copy stage `message generation`;
112
115
  `message-validation.md` is only an internal proof artifact.
113
116