@sellable/mcp 0.1.313 → 0.1.315

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.
@@ -62,6 +62,12 @@ const normalizeHost = (host) => {
62
62
  }
63
63
  return "unknown";
64
64
  };
65
+ const normalizeHostFromInput = (host, model) => {
66
+ const explicitHost = normalizeHost(host);
67
+ if (explicitHost !== "unknown")
68
+ return explicitHost;
69
+ return normalizeHost(model);
70
+ };
65
71
  const normalizeReasoning = (reasoning) => normalize(reasoning).replace(/[_\s-]+/g, "");
66
72
  const compareVersion = (candidate, minimum) => {
67
73
  const candidateParts = candidate.split(".").map((part) => Number(part));
@@ -78,6 +84,14 @@ const compareVersion = (candidate, minimum) => {
78
84
  return 0;
79
85
  };
80
86
  const extractModelVersions = (model) => Array.from(normalize(model).matchAll(/\b(\d+(?:\.\d+){0,2})\b/g)).map((match) => match[1]);
87
+ function modelContainsOtherHostFamily(model, currentHostConfig, config) {
88
+ const normalizedModel = normalize(model).replace(/[_-]+/g, " ");
89
+ if (!normalizedModel)
90
+ return false;
91
+ return Object.values(config.hosts)
92
+ .filter((hostConfig) => hostConfig !== currentHostConfig)
93
+ .some((hostConfig) => hostConfig.familyKeywords.some((keyword) => normalizedModel.includes(normalize(keyword))));
94
+ }
81
95
  function mergeConfig(rawConfig) {
82
96
  return {
83
97
  ...DEFAULT_MODEL_QUALITY_CONFIG,
@@ -126,11 +140,12 @@ function acceptsReasoning(reasoning, hostConfig) {
126
140
  .map((value) => normalizeReasoning(value))
127
141
  .includes(normalized);
128
142
  }
129
- function modelMeetsMinimum(model, hostConfig) {
143
+ function modelMeetsMinimum(model, hostConfig, options = {}) {
130
144
  const normalizedModel = normalize(model).replace(/[_-]+/g, " ");
131
145
  if (!normalizedModel)
132
146
  return false;
133
- const familyMatches = hostConfig.familyKeywords.every((keyword) => normalizedModel.includes(normalize(keyword)));
147
+ const familyMatches = options.familyKnownFromHost === true ||
148
+ hostConfig.familyKeywords.every((keyword) => normalizedModel.includes(normalize(keyword)));
134
149
  if (!familyMatches)
135
150
  return false;
136
151
  const versions = extractModelVersions(normalizedModel);
@@ -145,7 +160,10 @@ function findAcceptedHostConfig(host, model, config) {
145
160
  ["codex", config.hosts.codex],
146
161
  ]
147
162
  : [[host, config.hosts[host]]];
148
- return candidates.find(([, hostConfig]) => modelMeetsMinimum(model, hostConfig));
163
+ return candidates.find(([, hostConfig]) => modelMeetsMinimum(model, hostConfig, {
164
+ familyKnownFromHost: host !== "unknown" &&
165
+ !modelContainsOtherHostFamily(model, hostConfig, config),
166
+ }));
149
167
  }
150
168
  function looksLikeCodexStaleMetadata(host, model, reasoningEffort, config) {
151
169
  if (host !== "codex")
@@ -165,7 +183,7 @@ function looksLikeCodexStaleMetadata(host, model, reasoningEffort, config) {
165
183
  }
166
184
  export function evaluateCampaignModelQuality(input = {}) {
167
185
  const config = getCampaignModelQualityConfig();
168
- const host = normalizeHost([input.host, input.model].filter(Boolean).join(" "));
186
+ const host = normalizeHostFromInput(input.host, input.model);
169
187
  const model = input.model?.trim() || null;
170
188
  const reasoningEffort = input.reasoningEffort?.trim() || null;
171
189
  const recommendationHost = host === "claude" ? "claude" : "codex";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.313",
3
+ "version": "0.1.315",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -775,9 +775,10 @@ messages, and wait for final launch approval.
775
775
  What's your LinkedIn profile URL or handle?
776
776
  ```
777
777
 
778
- Do not silently ask Codex intake or approval questions as plain chat when
779
- `request_user_input` is unavailable in an interactive session. Stop and tell
780
- the user:
778
+ Codex only: do not silently ask intake or approval questions as plain chat when
779
+ `request_user_input` is unavailable in an interactive Codex session. Claude Code
780
+ uses `AskUserQuestion`; do not apply this Codex setup blocker in Claude Code.
781
+ In Codex, stop and tell the user:
781
782
 
782
783
  ```text
783
784
  I need Codex’s quick question panel to collect campaign inputs and approvals cleanly.
@@ -816,17 +817,18 @@ there.
816
817
  ## Bootstrap
817
818
 
818
819
  MCP tool access is required. First call `mcp__sellable__get_auth_status({})`
819
- directly. If that tool is unavailable, stop and say this is a Codex
820
- install/reload problem, not a campaign problem. Tell the user to
821
- run `curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh` so the
822
- packaged MCP server, Codex Desktop plugin, and Sellable skill bundle are
823
- installed. If they want an agent-readable checklist, tell them:
820
+ directly. If that tool is unavailable, stop and say this is a Sellable
821
+ install/reload problem for the current host, not a campaign problem. Tell the
822
+ user to run `curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh` so
823
+ the packaged MCP server and the current host integration are installed. If they
824
+ want an agent-readable checklist, tell them:
824
825
  `Install Sellable CLI and skills using https://app.sellable.dev/agent-install.txt`.
825
826
  For CLI verification, tell them to run
826
827
  `sellable --verify-only --host all --json --artifact "$HOME/.local/sellable/app-sellable-dev/installer/.last-verify.json"`.
827
- After that, they must fully quit and reopen Codex Desktop before starting a new
828
- thread. Do not use `scripts/mcp/sellable-tool-call.mjs`, `npm run`, `node`, or
829
- any local harness as a fallback for this interactive skill.
828
+ After that, they must fully quit and reopen the current host app before starting
829
+ a new thread. In Codex, say Codex Desktop. In Claude Code, say Claude Code. Do
830
+ not use `scripts/mcp/sellable-tool-call.mjs`, `npm run`, `node`, or any local
831
+ harness as a fallback for this interactive skill.
830
832
  Do not mention prompt loading, local skill files, missing linked versions,
831
833
  plugin cache paths, MCP namespaces, or runbooks in customer-facing progress
832
834
  updates.
@@ -938,8 +940,10 @@ updates.
938
940
  - Do not call `mcp__sellable__get_campaigns`.
939
941
  - Do not call `mcp__sellable__get_campaign` to hunt for IDs.
940
942
  - 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.
943
+ 6. Call `mcp__sellable__bootstrap_create_campaign({ flowVersion: "v2", campaignId?, host, model?, reasoningEffort? })`.
944
+ Pass the explicit current host label: `host: "Codex"` from Codex and
945
+ `host: "Claude Code"` from Claude Code. Also pass the current model and
946
+ reasoning when the host exposes them.
943
947
  7. If `safeToProceed !== true`, stop and show `blockingErrors` + `nextStep`.
944
948
  8. If `modelQuality.status === "warn"` and `modelQuality.metadataStale !== true`,
945
949
  show `modelQuality.message` before any setup/research and wait for the user