@sellable/install 0.1.6 → 0.1.7

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
@@ -30,6 +30,27 @@ Auth is stored once at:
30
30
 
31
31
  Claude Code and Codex are configured to launch the same packaged MCP server.
32
32
 
33
+ ## Names
34
+
35
+ Use the same public entrypoint in both hosts:
36
+
37
+ - Claude Code: `/sellable:create-campaign`
38
+ - Codex: `$sellable:create-campaign`
39
+ - Codex Desktop plugin: `sellable@sellable`
40
+ - Codex visible skill: `Sellable Create Campaign`
41
+ - Internal MCP workflow prompt: `create-campaign-v2`
42
+
43
+ Do not ask users to run `/sellable:create-campaign-v2`,
44
+ `$sellable:create-campaign-v2`, or `$sellable:sellable:create-campaign`.
45
+ `create-campaign-v2` is loaded internally by the skill.
46
+
47
+ ## Structured Questions
48
+
49
+ Claude Code uses `AskUserQuestion`. Codex uses `request_user_input` when that
50
+ tool is exposed in an interactive Plan/collaboration-mode session.
51
+ `codex exec` is non-interactive, so it cannot show the structured questionnaire
52
+ UI.
53
+
33
54
  For Codex Desktop, the installer also writes a local Sellable plugin bundle into
34
55
  `~/.sellable/codex-marketplace`, includes the Sellable skill entrypoints, and
35
56
  enables it in `~/.codex/config.toml`.
@@ -1,15 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from "node:child_process";
3
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
- import { dirname, join } from "node:path";
3
+ import {
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ rmSync,
8
+ writeFileSync,
9
+ } from "node:fs";
5
10
  import { homedir } from "node:os";
11
+ import { dirname, join } from "node:path";
6
12
  import { stdin as input, stdout as output } from "node:process";
7
13
  import { createInterface } from "node:readline/promises";
8
14
 
9
15
  const DEFAULT_API_URL = "https://app.sellable.dev";
10
16
  const DEFAULT_SERVER_PACKAGE =
11
17
  process.env.SELLABLE_MCP_PACKAGE || "@sellable/mcp";
12
- const CODEX_PLUGIN_VERSION = "0.1.6";
18
+ const CODEX_PLUGIN_VERSION = "0.1.7";
13
19
  const INSTALL_PACKAGE_SPEC = `@sellable/install@${CODEX_PLUGIN_VERSION}`;
14
20
 
15
21
  function usage() {
@@ -109,7 +115,9 @@ function redact(value) {
109
115
 
110
116
  function run(command, args, opts = {}) {
111
117
  const rendered = [command, ...args].join(" ");
112
- console.log(`+ ${rendered.replace(opts.token || "__NO_TOKEN__", "[redacted-token]")}`);
118
+ console.log(
119
+ `+ ${rendered.replace(opts.token || "__NO_TOKEN__", "[redacted-token]")}`
120
+ );
113
121
  if (opts.dryRun) return { status: 0, stdout: "", stderr: "" };
114
122
  const result = spawnSync(command, args, {
115
123
  encoding: "utf8",
@@ -186,8 +194,12 @@ async function promptForMissingAuth(opts) {
186
194
  }
187
195
 
188
196
  console.log("");
189
- console.log("Sellable needs one API token to connect Claude/Codex to your workspace.");
190
- console.log(`Open ${opts.apiUrl}/settings, create a token, then paste the values below.`);
197
+ console.log(
198
+ "Sellable needs one API token to connect Claude/Codex to your workspace."
199
+ );
200
+ console.log(
201
+ `Open ${opts.apiUrl}/settings, create a token, then paste the values below.`
202
+ );
191
203
  console.log(`Auth will be stored once at ${authPath()}.`);
192
204
  console.log("");
193
205
 
@@ -211,7 +223,11 @@ async function promptForMissingAuth(opts) {
211
223
  }
212
224
 
213
225
  function writeJson(path, data, opts) {
214
- const redacted = JSON.stringify({ ...data, token: redact(data.token) }, null, 2);
226
+ const redacted = JSON.stringify(
227
+ { ...data, token: redact(data.token) },
228
+ null,
229
+ 2
230
+ );
215
231
  console.log(`Writing ${path}: ${redacted}`);
216
232
  if (opts.dryRun) return;
217
233
  mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
@@ -246,7 +262,10 @@ function upsertTomlTable(content, tableName, block) {
246
262
  const normalizedBlock = block.trimEnd();
247
263
 
248
264
  if (tablePattern.test(content)) {
249
- return content.replace(tablePattern, (_, prefix) => `${prefix}${normalizedBlock}`);
265
+ return content.replace(
266
+ tablePattern,
267
+ (_, prefix) => `${prefix}${normalizedBlock}`
268
+ );
250
269
  }
251
270
 
252
271
  return `${content.trimEnd()}\n\n${normalizedBlock}\n`;
@@ -410,6 +429,38 @@ ${allowedToolsYaml(CREATE_CAMPAIGN_ALLOWED_TOOLS)}
410
429
 
411
430
  Use this as the customer-facing entrypoint for Sellable campaign creation.
412
431
 
432
+ ## Names To Use
433
+
434
+ Use these exact public names so Claude Code and Codex do not drift:
435
+
436
+ - Claude Code command: \`/sellable:create-campaign\`
437
+ - Codex skill command: \`$sellable:create-campaign\`
438
+ - Codex Desktop plugin: \`sellable@sellable\`
439
+ - Codex visible skill: \`Sellable Create Campaign\`
440
+ - Codex skill frontmatter name: \`create-campaign\`
441
+ - MCP server name: \`sellable\`
442
+ - Internal workflow prompt: \`create-campaign-v2\`
443
+
444
+ Do not tell users to run \`/sellable:create-campaign-v2\`,
445
+ \`$sellable:create-campaign-v2\`, or \`$sellable:sellable:create-campaign\`.
446
+ \`create-campaign-v2\` is only the internal subskill loaded through
447
+ \`mcp__sellable__get_subskill_prompt({ subskillName: "create-campaign-v2" })\`.
448
+
449
+ ## Structured Questions
450
+
451
+ Use the host-native structured question gate for intake and approval:
452
+
453
+ - Claude Code: \`AskUserQuestion\`
454
+ - Codex: \`request_user_input\` when exposed in an interactive
455
+ Plan/collaboration-mode session
456
+
457
+ Do not silently ask Codex intake or approval questions as plain chat when
458
+ \`request_user_input\` is unavailable in an interactive session. Stop and tell
459
+ the user to switch to Plan mode / collaboration mode, then rerun
460
+ \`$sellable:create-campaign\`. Plain chat questions are only acceptable in
461
+ non-interactive \`codex exec\` smoke/rehearsal runs because structured user
462
+ input is unavailable by design there.
463
+
413
464
  ## Bootstrap
414
465
 
415
466
  MCP tool access is required. First call \`mcp__sellable__get_auth_status({})\`
@@ -557,7 +608,12 @@ function installCodexDesktopPlugin(opts) {
557
608
  const home = codexHome();
558
609
  const configPath = join(home, "config.toml");
559
610
  const marketplaceRoot = join(homedir(), ".sellable", "codex-marketplace");
560
- const marketplacePath = join(marketplaceRoot, ".agents", "plugins", "marketplace.json");
611
+ const marketplacePath = join(
612
+ marketplaceRoot,
613
+ ".agents",
614
+ "plugins",
615
+ "marketplace.json"
616
+ );
561
617
  const pluginRoot = join(marketplaceRoot, "plugins", "sellable");
562
618
  const cacheRoot = join(home, "plugins", "cache", "sellable", "sellable");
563
619
  const pluginCache = join(cacheRoot, CODEX_PLUGIN_VERSION);
@@ -592,7 +648,11 @@ function installCodexDesktopPlugin(opts) {
592
648
  `${JSON.stringify(manifest, null, 2)}\n`,
593
649
  opts
594
650
  );
595
- writeFile(join(pluginRoot, ".mcp.json"), `${JSON.stringify(mcp, null, 2)}\n`, opts);
651
+ writeFile(
652
+ join(pluginRoot, ".mcp.json"),
653
+ `${JSON.stringify(mcp, null, 2)}\n`,
654
+ opts
655
+ );
596
656
  writeCodexPluginSkills(pluginRoot, opts);
597
657
 
598
658
  if (!opts.dryRun) {
@@ -603,12 +663,18 @@ function installCodexDesktopPlugin(opts) {
603
663
  `${JSON.stringify(manifest, null, 2)}\n`,
604
664
  opts
605
665
  );
606
- writeFile(join(pluginCache, ".mcp.json"), `${JSON.stringify(mcp, null, 2)}\n`, opts);
666
+ writeFile(
667
+ join(pluginCache, ".mcp.json"),
668
+ `${JSON.stringify(mcp, null, 2)}\n`,
669
+ opts
670
+ );
607
671
  writeCodexPluginSkills(pluginCache, opts);
608
672
 
609
673
  if (!opts.dryRun) {
610
674
  mkdirSync(home, { recursive: true, mode: 0o700 });
611
- let content = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
675
+ let content = existsSync(configPath)
676
+ ? readFileSync(configPath, "utf8")
677
+ : "";
612
678
  content = upsertTomlTable(
613
679
  content,
614
680
  "marketplaces.sellable",
@@ -633,7 +699,9 @@ enabled = false`
633
699
  } else {
634
700
  console.log(`+ upsert [marketplaces.sellable] in ${configPath}`);
635
701
  console.log(`+ enable [plugins."sellable@sellable"] in ${configPath}`);
636
- console.log(`+ disable stale [plugins."sellable@sellable-local"] in ${configPath}`);
702
+ console.log(
703
+ `+ disable stale [plugins."sellable@sellable-local"] in ${configPath}`
704
+ );
637
705
  }
638
706
 
639
707
  console.log("Codex Desktop plugin installed:");
@@ -656,7 +724,9 @@ function writeAuth(opts) {
656
724
  };
657
725
 
658
726
  if (opts.authFromExistingConfig) {
659
- console.log(`Leaving existing Sellable auth config unchanged: ${authPath()}`);
727
+ console.log(
728
+ `Leaving existing Sellable auth config unchanged: ${authPath()}`
729
+ );
660
730
  return;
661
731
  }
662
732
 
@@ -684,7 +754,8 @@ function mcpCommand(opts) {
684
754
 
685
755
  function installClaude(opts) {
686
756
  if (!commandExists("claude")) {
687
- const message = "Claude CLI not found. Install/login to Claude Code, then rerun: sellable --host claude";
757
+ const message =
758
+ "Claude CLI not found. Install/login to Claude Code, then rerun: sellable --host claude";
688
759
  if (opts.host === "all") {
689
760
  console.log(`Skipping Claude Code: ${message}`);
690
761
  return false;
@@ -692,7 +763,11 @@ function installClaude(opts) {
692
763
  throw new Error(message);
693
764
  }
694
765
  if (opts.server === "hosted") {
695
- run("claude", ["mcp", "add", "--transport", "http", "sellable", opts.hostedUrl], opts);
766
+ run(
767
+ "claude",
768
+ ["mcp", "add", "--transport", "http", "sellable", opts.hostedUrl],
769
+ opts
770
+ );
696
771
  return true;
697
772
  }
698
773
  const [command, args] = mcpCommand(opts);
@@ -701,13 +776,18 @@ function installClaude(opts) {
701
776
  dryRun: opts.dryRun,
702
777
  allowFail: true,
703
778
  });
704
- run("claude", ["mcp", "add", "--transport", "stdio", "sellable", "--", command, ...args], opts);
779
+ run(
780
+ "claude",
781
+ ["mcp", "add", "--transport", "stdio", "sellable", "--", command, ...args],
782
+ opts
783
+ );
705
784
  return true;
706
785
  }
707
786
 
708
787
  function installCodex(opts) {
709
788
  if (!commandExists("codex")) {
710
- const message = "Codex CLI not found. Install/login to Codex, then rerun: sellable --host codex";
789
+ const message =
790
+ "Codex CLI not found. Install/login to Codex, then rerun: sellable --host codex";
711
791
  if (opts.host === "all") {
712
792
  console.log(`Skipping Codex: ${message}`);
713
793
  return false;
@@ -733,14 +813,20 @@ function installCodex(opts) {
733
813
  function verify(opts) {
734
814
  const stored = readStoredAuth();
735
815
  if (!stored?.token || !stored?.workspaceId) {
736
- throw new Error(`Sellable auth config missing or incomplete: ${authPath()}`);
816
+ throw new Error(
817
+ `Sellable auth config missing or incomplete: ${authPath()}`
818
+ );
737
819
  }
738
820
  console.log(`Sellable auth config present: ${authPath()}`);
739
821
  if (opts.host === "claude" || opts.host === "all") {
740
- console.log(commandExists("claude") ? "Claude CLI present" : "Claude CLI missing");
822
+ console.log(
823
+ commandExists("claude") ? "Claude CLI present" : "Claude CLI missing"
824
+ );
741
825
  }
742
826
  if (opts.host === "codex" || opts.host === "all") {
743
- console.log(commandExists("codex") ? "Codex CLI present" : "Codex CLI missing");
827
+ console.log(
828
+ commandExists("codex") ? "Codex CLI present" : "Codex CLI missing"
829
+ );
744
830
  const pluginPath = join(
745
831
  codexHome(),
746
832
  "plugins",
@@ -762,8 +848,16 @@ function verify(opts) {
762
848
  "sellable-create-campaign",
763
849
  "SKILL.md"
764
850
  );
765
- console.log(existsSync(pluginPath) ? "Codex Desktop plugin present" : "Codex Desktop plugin missing");
766
- console.log(existsSync(skillPath) ? "Codex skill bundle present" : "Codex skill bundle missing");
851
+ console.log(
852
+ existsSync(pluginPath)
853
+ ? "Codex Desktop plugin present"
854
+ : "Codex Desktop plugin missing"
855
+ );
856
+ console.log(
857
+ existsSync(skillPath)
858
+ ? "Codex skill bundle present"
859
+ : "Codex skill bundle missing"
860
+ );
767
861
  }
768
862
  }
769
863
 
@@ -771,11 +865,17 @@ function printNextSteps(installedHosts) {
771
865
  console.log("");
772
866
  console.log("Next steps:");
773
867
  if (installedHosts.length > 0) {
774
- console.log(`1. Fully quit and reopen ${installedHosts.join(" and ")} so MCP tools reload.`);
868
+ console.log(
869
+ `1. Fully quit and reopen ${installedHosts.join(" and ")} so MCP tools reload.`
870
+ );
775
871
  console.log("2. Start a new thread and choose Sellable Create Campaign.");
776
- console.log("3. If tools do not appear, run: sellable --verify-only --host all");
872
+ console.log(
873
+ "3. If tools do not appear, run: sellable --verify-only --host all"
874
+ );
777
875
  } else {
778
- console.log("1. Install Claude Code or Codex, then rerun this installer for that host.");
876
+ console.log(
877
+ "1. Install Claude Code or Codex, then rerun this installer for that host."
878
+ );
779
879
  console.log("2. Verify auth later with: sellable --verify-only --host all");
780
880
  }
781
881
  }
@@ -806,7 +906,9 @@ async function main() {
806
906
  }
807
907
  }
808
908
  if (opts.dryRun) {
809
- console.log("Dry run complete; verification skipped because no files were written.");
909
+ console.log(
910
+ "Dry run complete; verification skipped because no files were written."
911
+ );
810
912
  } else {
811
913
  verify(opts);
812
914
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code and Codex",
6
6
  "bin": {