@sellable/install 0.1.210 → 0.1.212

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.
@@ -72,8 +72,9 @@ const CODEX_PLUGIN_COMPAT_VERSIONS = [
72
72
  "0.1.40",
73
73
  "0.1.41",
74
74
  ];
75
- const INSTALL_PACKAGE_SPEC =
75
+ const RAW_INSTALL_PACKAGE_SPEC =
76
76
  process.env.SELLABLE_INSTALL_PACKAGE_SPEC || "@sellable/install@latest";
77
+ const INSTALL_PACKAGE_SPEC = normalizeWindowsPackageSpec(RAW_INSTALL_PACKAGE_SPEC);
77
78
 
78
79
  const useColor = Boolean(output.isTTY) && process.env.NO_COLOR === undefined;
79
80
  const C = {
@@ -89,6 +90,15 @@ const C = {
89
90
 
90
91
  let VERBOSE = false;
91
92
 
93
+ function normalizeWindowsPackageSpec(value) {
94
+ if (typeof value !== "string") return value;
95
+ if (/^[A-Za-z]:\//.test(value)) return value.replace(/\//g, "\\");
96
+ if (/^\/[A-Za-z]\//.test(value)) {
97
+ return `${value[1]}:${value.slice(2)}`.replace(/\//g, "\\");
98
+ }
99
+ return value;
100
+ }
101
+
92
102
  function logVerbose(line) {
93
103
  if (VERBOSE) console.log(line);
94
104
  }
@@ -263,7 +273,7 @@ function parseArgs(argv) {
263
273
  } else if (arg === "--api-url") {
264
274
  opts.apiUrl = next();
265
275
  } else if (arg === "--mcp-package") {
266
- opts.mcpPackage = next();
276
+ opts.mcpPackage = normalizeWindowsPackageSpec(next());
267
277
  } else if (arg === "--local-command") {
268
278
  opts.localCommand = next();
269
279
  } else if (arg === "--hosted-url") {
@@ -771,7 +781,54 @@ function codexSkillOpenAiYaml(displayName, description) {
771
781
  `;
772
782
  }
773
783
 
774
- function createCampaignSkillMd() {
784
+ function installedHostContract(host, commandName) {
785
+ if (host === "codex") {
786
+ return `## Installed Host Contract
787
+
788
+ This installed skill is running in Codex. When the shared workflow body or
789
+ fallback text mentions both Claude Code and Codex for internal parity, choose
790
+ the Codex instruction for customer-facing language and host functions.
791
+
792
+ - Customer-facing command: \`$sellable:${commandName}\`
793
+ - Structured question function: \`request_user_input\`
794
+ - Bootstrap host label: \`host: "Codex"\`
795
+ - Install/reload blocker label: Codex install/reload problem
796
+ - Reload instruction: fully quit and reopen Codex Desktop, then start a new thread
797
+
798
+ Do not tell Codex users to run \`/sellable:${commandName}\`, use
799
+ \`AskUserQuestion\`, or restart Claude Code. Do not describe this run as Claude Code.`;
800
+ }
801
+ if (host === "claude") {
802
+ return `## Installed Host Contract
803
+
804
+ This installed command is running in Claude Code. When the shared workflow body
805
+ or fallback text mentions both Claude Code and Codex for internal parity, choose
806
+ the Claude Code instruction for customer-facing language and host functions.
807
+
808
+ - Customer-facing command: \`/sellable:${commandName}\`
809
+ - Structured question function: \`AskUserQuestion\`
810
+ - Bootstrap host label: \`host: "Claude Code"\`
811
+ - Install/reload blocker label: Claude Code install/reload problem
812
+ - Reload instruction: fully quit and reopen Claude Code, then start a new session
813
+
814
+ Do not tell Claude Code users to run \`$sellable:${commandName}\`, use
815
+ \`request_user_input\`, or restart Codex Desktop. Do not describe this run as Codex.`;
816
+ }
817
+ return "";
818
+ }
819
+
820
+ function stampInstalledHost(markdown, host, commandName) {
821
+ const preamble = installedHostContract(host, commandName);
822
+ if (!preamble || markdown.includes("## Installed Host Contract")) {
823
+ return markdown;
824
+ }
825
+ return String(markdown).replace(
826
+ /(^# .+\n\n)/m,
827
+ `$1${preamble}\n\n`
828
+ );
829
+ }
830
+
831
+ function createCampaignSkillMd(host = "shared") {
775
832
  // Single source of truth: ../skill-templates/create-campaign.md, which is
776
833
  // copied from the canonical mcp/sellable/skills/create-campaign/SKILL.md at
777
834
  // publish time via scripts/sync-skill-templates.mjs (prepublishOnly hook).
@@ -786,12 +843,16 @@ function createCampaignSkillMd() {
786
843
  "create-campaign.md"
787
844
  );
788
845
  if (existsSync(templatePath)) {
789
- return readFileSync(templatePath, "utf8");
846
+ return stampInstalledHost(
847
+ readFileSync(templatePath, "utf8"),
848
+ host,
849
+ "create-campaign"
850
+ );
790
851
  }
791
852
  } catch {
792
853
  // fall through to hardcoded fallback below
793
854
  }
794
- return `---
855
+ return stampInstalledHost(`---
795
856
  name: create-campaign
796
857
  description: Create a Sellable campaign through the approval-gated workflow.
797
858
  allowed-tools:
@@ -814,17 +875,11 @@ person/company this campaign is for, then I’ll turn that into a campaign brief
814
875
  before anything is created.
815
876
  \`\`\`
816
877
 
817
- Exception: if \`bootstrap_create_campaign.modelQuality.status === "warn"\` and
818
- \`bootstrap_create_campaign.modelQuality.metadataStale !== true\`, the first
819
- visible campaign message must be the model-quality warning from
820
- \`modelQuality.message\`. Ask the user to switch to the configured minimum model
821
- or explicitly continue anyway before identity setup, research, lead filtering,
822
- message generation, or launch review. If \`metadataStale === true\`, continue
823
- normally and do not ask the user to switch.
824
-
825
- If \`bootstrap_create_campaign.modelQuality.metadataStale === true\`, continue
826
- normally. Do not ask the user to switch models; this is an accepted Codex host
827
- metadata mismatch.
878
+ Exception: if \`bootstrap_create_campaign.modelQuality.status === "warn"\`, the
879
+ first visible campaign message must be the model-quality warning from
880
+ \`modelQuality.message\`. Only trust that warning when bootstrap received active
881
+ turn/runtime metadata or an explicit user-confirmed model. Do not warn from
882
+ config defaults, stale host labels, or inferred model names.
828
883
 
829
884
  If a linked/local skill file is stale or missing, silently use the installed
830
885
  \`sellable@sellable\` plugin copy. Do not tell the user about the stale link,
@@ -842,6 +897,56 @@ clear business decisions, tradeoffs, and approval gates. Use product language:
842
897
  - "I can create a draft shell for you to watch with approval gates before
843
898
  sourcing", not mutation jargon
844
899
 
900
+ ## Active Model Metadata
901
+
902
+ Before calling \`bootstrap_create_campaign\`, collect the active host model data
903
+ only from a source that can describe this turn:
904
+
905
+ - Codex: if \`mcp__node_repl__js\` is available, inspect only
906
+ \`nodeRepl.requestMeta["x-codex-turn-metadata"].model\` and
907
+ \`nodeRepl.requestMeta["x-codex-turn-metadata"].reasoning_effort\`. Pass those
908
+ values as \`model\` and \`reasoningEffort\`, with
909
+ \`modelMetadataSource: "codex_turn_metadata"\`.
910
+ - Codex fallback/cross-check: if active turn metadata is unavailable, you may
911
+ inspect \`~/.codex/config.toml\` for \`model\` and
912
+ \`model_reasoning_effort\`, but pass
913
+ \`modelMetadataSource: "codex_config_fallback"\` and do not treat it as a
914
+ reason to ask the user to switch models. Active turn metadata wins if it
915
+ differs from config.
916
+ - Claude Code / Opus: use active Claude runtime metadata only if the current
917
+ host exposes model and effort for this same session, with
918
+ \`modelMetadataSource: "claude_runtime_metadata"\`. Current Claude Code MCP
919
+ tool calls may not include model or effort metadata; when they do not, omit
920
+ \`model\`, \`reasoningEffort\`, and \`modelMetadataSource\`.
921
+ - Claude Code session-context self-report: if your current Claude session
922
+ context explicitly states both the exact model ID and effort/thinking level,
923
+ report it internally in this shape and pass it to bootstrap with
924
+ \`modelMetadataSource: "claude_session_context"\`:
925
+
926
+ \`\`\`text
927
+ - Model (name): ...
928
+ - Model (ID): ...
929
+ - Reasoning effort: ...
930
+ - Source: active Claude Code session context
931
+ \`\`\`
932
+
933
+ Use this only when the values are explicitly present in the current session
934
+ context. Do not infer an ID from the friendly name, do not infer effort from
935
+ \`alwaysThinkingEnabled\`, and do not show this self-report to the user during
936
+ normal campaign setup.
937
+ - Do not run a nested \`claude -p\`, inspect \`~/.claude/settings.json\`, or read
938
+ Claude CLI defaults as proof of the user's active Claude Code session. Those
939
+ checks can validate a new child session or saved defaults, but not this
940
+ session's actual model and effort.
941
+ - If the user explicitly provides active Claude \`/status\` or \`/model\` output
942
+ that includes both model and effort, pass it with
943
+ \`modelMetadataSource: "user_confirmed"\`. If it is missing either model or
944
+ effort, treat the metadata as unknown and continue.
945
+
946
+ Never invent the model or reasoning effort. Never pass config defaults as active
947
+ metadata. If bootstrap returns \`modelQuality.status === "unknown"\`, continue
948
+ without asking the user to switch models.
949
+
845
950
  When explaining source decisions, show the concrete counts behind the
846
951
  logic: lanes searched, timeframe, raw result counts, finalist posts or preview
847
952
  rows, sampled people, sampled fits as n/N (%), estimated usable people, and the
@@ -1263,13 +1368,19 @@ updates.
1263
1368
  - Do not call \`mcp__sellable__get_campaigns\`.
1264
1369
  - Do not call \`mcp__sellable__get_campaign\` to hunt for IDs.
1265
1370
  - Do not call \`mcp__sellable__create_campaign({ campaignId: ... })\` unless the user supplied that id.
1266
- 5. Call \`mcp__sellable__bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host?, model?, reasoningEffort? })\`.
1267
- Pass the current host, model, and reasoning when the host exposes them.
1371
+ 5. Call \`mcp__sellable__bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host?, model?, reasoningEffort?, modelMetadataSource? })\`.
1372
+ Pass model metadata only when collected by the Active Model Metadata rules
1373
+ above. For Codex active turn metadata, pass
1374
+ \`modelMetadataSource: "codex_turn_metadata"\`. For explicit Claude session
1375
+ context, pass \`modelMetadataSource: "claude_session_context"\`. For explicit
1376
+ user-confirmed Claude \`/status\` or \`/model\` output, pass
1377
+ \`modelMetadataSource: "user_confirmed"\` only when it includes both model and
1378
+ effort.
1268
1379
  6. If \`safeToProceed !== true\`, stop and show \`blockingErrors\` + \`nextStep\`.
1269
- 7. If \`modelQuality.status === "warn"\` and \`modelQuality.metadataStale !== true\`,
1270
- show \`modelQuality.message\` before any setup/research and wait for the user
1271
- to switch or explicitly continue. If \`metadataStale === true\`, continue
1272
- normally and do not tell the user to switch.
1380
+ 7. If \`modelQuality.status === "warn"\`, show \`modelQuality.message\` before
1381
+ any setup/research and wait for the user to switch or explicitly continue. If
1382
+ \`modelQuality.status === "unknown"\`, continue; do not tell the user to
1383
+ switch models.
1273
1384
 
1274
1385
  ## Execute Workflow
1275
1386
 
@@ -1350,10 +1461,10 @@ updates.
1350
1461
  If subskill lookup fails, use
1351
1462
  \`mcp__sellable__search_subskill_prompts({ query: "create-campaign-v2" })\`,
1352
1463
  then retry \`get_subskill_prompt\`.
1353
- `;
1464
+ `, host, "create-campaign");
1354
1465
  }
1355
1466
 
1356
- function createAbTestSkillMd() {
1467
+ function createAbTestSkillMd(host = "shared") {
1357
1468
  try {
1358
1469
  const here = dirname(fileURLToPath(import.meta.url));
1359
1470
  const templatePath = join(
@@ -1363,12 +1474,16 @@ function createAbTestSkillMd() {
1363
1474
  "create-ab-test.md"
1364
1475
  );
1365
1476
  if (existsSync(templatePath)) {
1366
- return readFileSync(templatePath, "utf8");
1477
+ return stampInstalledHost(
1478
+ readFileSync(templatePath, "utf8"),
1479
+ host,
1480
+ "create-ab-test"
1481
+ );
1367
1482
  }
1368
1483
  } catch {
1369
1484
  // fall through to hardcoded fallback below
1370
1485
  }
1371
- return `---
1486
+ return stampInstalledHost(`---
1372
1487
  name: create-ab-test
1373
1488
  description: Create a Sellable campaign A/B test from a clean source lead list.
1374
1489
  visibility: public
@@ -1384,11 +1499,11 @@ allowed-tools:
1384
1499
  # Sellable Create A/B Test
1385
1500
 
1386
1501
  Use \`prepare_campaign_ab_test\` to create clean A/B split lead lists and review-copy campaigns. Do not call \`export_table_csv\` from generated campaign workflow tables as lead source data, and do not call \`start_campaign\`.
1387
- `;
1502
+ `, host, "create-ab-test");
1388
1503
  }
1389
1504
 
1390
- function genericSellableSkillMd({ name, title, description }) {
1391
- return `---
1505
+ function genericSellableSkillMd({ name, title, description, host = "shared" }) {
1506
+ return stampInstalledHost(`---
1392
1507
  name: ${name}
1393
1508
  description: ${yamlString(description)}
1394
1509
  allowed-tools:
@@ -1423,11 +1538,11 @@ Desktop, then start a new thread.
1423
1538
  ## MCP Prompt Fallback
1424
1539
 
1425
1540
  If subskill lookup fails, use \`mcp__sellable__search_subskill_prompts({ query: "${name}" })\`, then retry \`get_subskill_prompt\`.
1426
- `;
1541
+ `, host, name);
1427
1542
  }
1428
1543
 
1429
- function foundationSkillMd() {
1430
- return `---
1544
+ function foundationSkillMd(host = "shared") {
1545
+ return stampInstalledHost(`---
1431
1546
  name: foundation
1432
1547
  description: ${yamlString(FOUNDATION_SKILL_DESCRIPTION)}
1433
1548
  allowed-tools:
@@ -1486,11 +1601,11 @@ Desktop, then start a new thread.
1486
1601
  If exact subskill lookup fails, use
1487
1602
  \`mcp__sellable__search_subskill_prompts({ query: "foundation", includePublic: true, includeInternal: true })\`,
1488
1603
  then retry \`get_subskill_prompt\`.
1489
- `;
1604
+ `, host, "foundation");
1490
1605
  }
1491
1606
 
1492
- function contentSkillMd() {
1493
- return `---
1607
+ function contentSkillMd(host = "shared") {
1608
+ return stampInstalledHost(`---
1494
1609
  name: content
1495
1610
  description: Add transcripts and rough ideas, cluster recurring themes, ideate post seeds, and hand draft requests to create-post.
1496
1611
  allowed-tools:
@@ -1556,11 +1671,11 @@ Desktop, then start a new thread.
1556
1671
  If exact subskill lookup fails, use
1557
1672
  \`mcp__sellable__search_subskill_prompts({ query: "content", includePublic: true, includeInternal: true })\`,
1558
1673
  then retry \`get_subskill_prompt\`.
1559
- `;
1674
+ `, host, "content");
1560
1675
  }
1561
1676
 
1562
- function createPostSkillMd() {
1563
- return `---
1677
+ function createPostSkillMd(host = "shared") {
1678
+ return stampInstalledHost(`---
1564
1679
  name: create-post
1565
1680
  description: Capture rough LinkedIn post ideas, develop a valuable premise, research current hooks and audience tension, then save validated drafts in the user's voice.
1566
1681
  allowed-tools:
@@ -1630,7 +1745,7 @@ Desktop, then start a new thread.
1630
1745
  If exact subskill lookup fails, use
1631
1746
  \`mcp__sellable__search_subskill_prompts({ query: "create-post", includePublic: true, includeInternal: true })\`,
1632
1747
  then retry \`get_subskill_prompt\`.
1633
- `;
1748
+ `, host, "create-post");
1634
1749
  }
1635
1750
 
1636
1751
  function createCampaignSoulMd() {
@@ -1827,7 +1942,7 @@ function codexPluginSkills() {
1827
1942
  dir: "sellable-create-campaign",
1828
1943
  displayName: "Sellable Create Campaign",
1829
1944
  description: "Create a Sellable campaign with approval gates",
1830
- skillMd: createCampaignSkillMd(),
1945
+ skillMd: createCampaignSkillMd("codex"),
1831
1946
  soulMd: createCampaignSoulMd(),
1832
1947
  },
1833
1948
  {
@@ -2051,7 +2166,7 @@ function claudeCommands() {
2051
2166
  filename: join("sellable", "create-campaign.md"),
2052
2167
  description: "Create a Sellable campaign through the approval-gated workflow.",
2053
2168
  argumentHint: "[campaign goal, company, or offer]",
2054
- skillMd: createCampaignSkillMd(),
2169
+ skillMd: createCampaignSkillMd("claude"),
2055
2170
  allowedTools: ["AskUserQuestion", "Task", ...CREATE_CAMPAIGN_ALLOWED_TOOLS],
2056
2171
  },
2057
2172
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.210",
3
+ "version": "0.1.212",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {
@@ -179,17 +179,11 @@ person/company this campaign is for, then I’ll turn that into a campaign brief
179
179
  before we move into lead sourcing.
180
180
  ```
181
181
 
182
- Exception: if `bootstrap_create_campaign.modelQuality.status === "warn"` and
183
- `bootstrap_create_campaign.modelQuality.metadataStale !== true`, the first
184
- visible campaign message must be the model-quality warning from
185
- `modelQuality.message`. Ask the user to switch to the configured minimum model
186
- or explicitly continue anyway before identity setup, research, lead filtering,
187
- message generation, or launch review. If `metadataStale === true`, continue
188
- normally and do not ask the user to switch.
189
-
190
- If `bootstrap_create_campaign.modelQuality.metadataStale === true`, continue
191
- normally. Do not ask the user to switch models; this is an accepted Codex host
192
- metadata mismatch.
182
+ Exception: if `bootstrap_create_campaign.modelQuality.status === "warn"`, the
183
+ first visible campaign message must be the model-quality warning from
184
+ `modelQuality.message`. Only trust that warning when bootstrap received active
185
+ turn/runtime metadata or an explicit user-confirmed model. Do not warn from
186
+ config defaults, stale host labels, or inferred model names.
193
187
 
194
188
  If a linked/local skill file is stale or missing, silently use the installed
195
189
  `sellable@sellable` plugin copy. Do not tell the user about the stale link,
@@ -207,6 +201,55 @@ clear business decisions, tradeoffs, and approval gates. Use product language:
207
201
  - "I can create a draft shell for you to watch with approval gates before
208
202
  sourcing", not mutation jargon
209
203
 
204
+ ## Active Model Metadata
205
+
206
+ Before calling `bootstrap_create_campaign`, collect the active host model data
207
+ only from a source that can describe this turn:
208
+
209
+ - Codex: if `mcp__node_repl__js` is available, inspect only
210
+ `nodeRepl.requestMeta["x-codex-turn-metadata"].model` and
211
+ `nodeRepl.requestMeta["x-codex-turn-metadata"].reasoning_effort`. Pass those
212
+ values as `model` and `reasoningEffort`, with
213
+ `modelMetadataSource: "codex_turn_metadata"`.
214
+ - Codex fallback/cross-check: if active turn metadata is unavailable, you may
215
+ inspect `~/.codex/config.toml` for `model` and `model_reasoning_effort`, but
216
+ pass `modelMetadataSource: "codex_config_fallback"` and do not treat it as a
217
+ reason to ask the user to switch models. Active turn metadata wins if it
218
+ differs from config.
219
+ - Claude Code / Opus: use active Claude runtime metadata only if the current
220
+ host exposes model and effort for this same session, with
221
+ `modelMetadataSource: "claude_runtime_metadata"`. Current Claude Code MCP
222
+ tool calls may not include model or effort metadata; when they do not, omit
223
+ `model`, `reasoningEffort`, and `modelMetadataSource`.
224
+ - Claude Code session-context self-report: if your current Claude session
225
+ context explicitly states both the exact model ID and effort/thinking level,
226
+ report it internally in this shape and pass it to bootstrap with
227
+ `modelMetadataSource: "claude_session_context"`:
228
+
229
+ ```text
230
+ - Model (name): ...
231
+ - Model (ID): ...
232
+ - Reasoning effort: ...
233
+ - Source: active Claude Code session context
234
+ ```
235
+
236
+ Use this only when the values are explicitly present in the current session
237
+ context. Do not infer an ID from the friendly name, do not infer effort from
238
+ `alwaysThinkingEnabled`, and do not show this self-report to the user during
239
+ normal campaign setup.
240
+ - Do not run a nested `claude -p`, inspect `~/.claude/settings.json`, or read
241
+ Claude CLI defaults as proof of the user's active Claude Code session. Those
242
+ checks can validate a new child session or saved defaults, but not this
243
+ session's actual model and effort.
244
+ - If the user explicitly provides active Claude `/status` or `/model` output
245
+ that includes both model and effort, pass it with
246
+ `modelMetadataSource: "user_confirmed"`. If it is missing either model or
247
+ effort, treat the metadata as unknown and continue.
248
+
249
+ Never invent the model or reasoning effort. Never pass config defaults as active
250
+ metadata. If bootstrap returns `modelQuality.status === "unknown"`, continue
251
+ without asking the user to switch models.
252
+
210
253
  Approval and safety copy should be tasteful. State what the current approval
211
254
  covers once, in one short sentence, then move on. Do not append repeated
212
255
  "nothing starts / no leads import / no sending" disclaimers to routine progress
@@ -938,13 +981,19 @@ updates.
938
981
  - Do not call `mcp__sellable__get_campaigns`.
939
982
  - Do not call `mcp__sellable__get_campaign` to hunt for IDs.
940
983
  - Do not call `mcp__sellable__create_campaign({ campaignId: ... })` unless the user supplied that id.
941
- 6. Call `mcp__sellable__bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host?, model?, reasoningEffort? })`.
942
- Pass the current host, model, and reasoning when the host exposes them.
984
+ 6. Call `mcp__sellable__bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host?, model?, reasoningEffort?, modelMetadataSource? })`.
985
+ Pass model metadata only when collected by the Active Model Metadata rules
986
+ above. For Codex active turn metadata, pass
987
+ `modelMetadataSource: "codex_turn_metadata"`. For explicit Claude session
988
+ context, pass `modelMetadataSource: "claude_session_context"`. For explicit
989
+ user-confirmed Claude `/status` or `/model` output, pass
990
+ `modelMetadataSource: "user_confirmed"` only when it includes both model and
991
+ effort.
943
992
  7. If `safeToProceed !== true`, stop and show `blockingErrors` + `nextStep`.
944
- 8. If `modelQuality.status === "warn"` and `modelQuality.metadataStale !== true`,
945
- show `modelQuality.message` before any setup/research and wait for the user
946
- to switch or explicitly continue. If `metadataStale === true`, continue
947
- normally and do not tell the user to switch.
993
+ 8. If `modelQuality.status === "warn"`, show `modelQuality.message` before any
994
+ setup/research and wait for the user to switch or explicitly continue. If
995
+ `modelQuality.status === "unknown"`, continue; do not tell the user to
996
+ switch models.
948
997
 
949
998
  ## Execute Workflow
950
999