@sellable/install 0.1.302 → 0.1.303
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 +10 -27
- package/bin/sellable-install.mjs +21 -659
- package/package.json +3 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sellable Installer
|
|
2
2
|
|
|
3
|
-
Installs Sellable MCP for Claude Code
|
|
3
|
+
Installs Sellable MCP for Claude Code and Codex.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh
|
|
@@ -16,9 +16,7 @@ Windows users can use PowerShell or Windows Terminal:
|
|
|
16
16
|
iwr "https://app.sellable.dev/api/v2/cli/install.ps1" | iex
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
After install, restart
|
|
20
|
-
tools and skills reload. In an active Hermes session, `/reload-mcp` and
|
|
21
|
-
`/reload-skills` can reload the new config.
|
|
19
|
+
After install, restart Codex Desktop so the Sellable skill appears.
|
|
22
20
|
|
|
23
21
|
Verify the runtime tools with:
|
|
24
22
|
|
|
@@ -34,11 +32,11 @@ sellable create
|
|
|
34
32
|
```
|
|
35
33
|
|
|
36
34
|
Campaign creation, sender send refills, foundation memory, content
|
|
37
|
-
capture/ideation, and post drafting run inside Claude Code
|
|
38
|
-
|
|
35
|
+
capture/ideation, and post drafting run inside Claude Code or Codex, where the
|
|
36
|
+
Sellable MCP tools and approval flows are available.
|
|
39
37
|
|
|
40
38
|
Install is auth-free by default. The normal path is first-run login: launch a
|
|
41
|
-
Sellable workflow in Claude Code
|
|
39
|
+
Sellable workflow in Claude Code or Codex and the agent handles Sellable
|
|
42
40
|
sign-in with a browser magic-link handoff.
|
|
43
41
|
|
|
44
42
|
The installer uses package stdio MCP by default:
|
|
@@ -47,7 +45,7 @@ The installer uses package stdio MCP by default:
|
|
|
47
45
|
npm exec --yes --package @sellable/mcp@latest -- sellable-mcp
|
|
48
46
|
```
|
|
49
47
|
|
|
50
|
-
That keeps new Claude Code/Codex
|
|
48
|
+
That keeps new Claude Code/Codex MCP starts on the latest stable package. The
|
|
51
49
|
MCP server also checks npm at startup and during `get_auth_status`, caching the
|
|
52
50
|
result at `~/.sellable/update-check.json` so users are prompted to rerun the
|
|
53
51
|
latest installer only when an update is actually available.
|
|
@@ -83,7 +81,7 @@ Auth is stored once at:
|
|
|
83
81
|
~/.sellable/config.json
|
|
84
82
|
```
|
|
85
83
|
|
|
86
|
-
Claude Code
|
|
84
|
+
Claude Code and Codex are configured to launch the same packaged MCP server. The
|
|
87
85
|
installer also writes Sellable agent definitions from the packaged `agents/`
|
|
88
86
|
registry, but normal create-campaign runs use only the Message Drafting
|
|
89
87
|
background agent (`post-find-leads-message-scout`). Source discovery and
|
|
@@ -94,7 +92,7 @@ source-scout agents.
|
|
|
94
92
|
|
|
95
93
|
## Names
|
|
96
94
|
|
|
97
|
-
Use the same public entrypoints
|
|
95
|
+
Use the same public entrypoints in both hosts:
|
|
98
96
|
|
|
99
97
|
- Claude Code: `/sellable:create-campaign`
|
|
100
98
|
- Claude Code: `/sellable:create-ab-test`
|
|
@@ -112,14 +110,6 @@ Use the same public entrypoints across supported hosts:
|
|
|
112
110
|
- Codex: `$sellable:create-post`
|
|
113
111
|
- Codex: `$sellable:refresh-sender-engagement`
|
|
114
112
|
- Codex: `$sellable:refill-sends`
|
|
115
|
-
- Hermes: `/sellable-create-campaign`
|
|
116
|
-
- Hermes: `/sellable-create-ab-test`
|
|
117
|
-
- Hermes: `/sellable-create-evergreen-campaigns`
|
|
118
|
-
- Hermes: `/sellable-foundation`
|
|
119
|
-
- Hermes: `/sellable-content`
|
|
120
|
-
- Hermes: `/sellable-create-post`
|
|
121
|
-
- Hermes: `/sellable-refresh-sender-engagement`
|
|
122
|
-
- Hermes: `/sellable-refill-sends`
|
|
123
113
|
- Codex Desktop plugin: `sellable@sellable`
|
|
124
114
|
- Codex visible skill: `Sellable Create Campaign`
|
|
125
115
|
- Codex visible skill: `Sellable Create A/B Test`
|
|
@@ -135,8 +125,7 @@ Use the same public entrypoints across supported hosts:
|
|
|
135
125
|
|
|
136
126
|
Do not ask users to run `/sellable:create-campaign-v2`,
|
|
137
127
|
`$sellable:create-campaign-v2`, `$sellable:load-voice`, or
|
|
138
|
-
`$sellable:sellable:create-campaign`.
|
|
139
|
-
`/sellable:create-campaign`; use `/sellable-create-campaign`. `create-campaign-v2` is loaded internally
|
|
128
|
+
`$sellable:sellable:create-campaign`. `create-campaign-v2` is loaded internally
|
|
140
129
|
by the campaign skill. `foundation` is the preferred entrypoint for durable
|
|
141
130
|
founder/company memory. `content` is the preferred entrypoint for adding
|
|
142
131
|
transcripts, recurring ideas, and post seeds. `create-post` remains a supported
|
|
@@ -151,12 +140,6 @@ mode support by writing `default_mode_request_user_input = true` under
|
|
|
151
140
|
`[features]` in `~/.codex/config.toml`. `codex exec` is non-interactive, so it
|
|
152
141
|
cannot show the structured questionnaire UI.
|
|
153
142
|
|
|
154
|
-
Hermes skills use plain chat for setup and approval questions unless a
|
|
155
|
-
Hermes-native approval tool is visible in the current session. Hermes exposes
|
|
156
|
-
Sellable MCP tools as `mcp_sellable_<tool>`, for example
|
|
157
|
-
`mcp_sellable_get_auth_status`, `mcp_sellable_start_cli_login`, and
|
|
158
|
-
`mcp_sellable_wait_for_cli_login`.
|
|
159
|
-
|
|
160
143
|
For Codex Desktop, the installer also writes a local Sellable plugin bundle into
|
|
161
144
|
`~/.sellable/codex-marketplace`, includes the Sellable skill entrypoints, and
|
|
162
145
|
enables it in `~/.codex/config.toml`.
|
|
@@ -174,4 +157,4 @@ The installer writes compatibility cache aliases for recent Codex plugin
|
|
|
174
157
|
versions so stale Desktop skill links resolve to the current wrapper instead of
|
|
175
158
|
showing file/version debugging to the user.
|
|
176
159
|
|
|
177
|
-
If only one host is installed, `--host all` installs the available host and tells you how to add the
|
|
160
|
+
If only one host is installed, `--host all` installs the available host and tells you how to add the other one later.
|
package/bin/sellable-install.mjs
CHANGED
|
@@ -15,7 +15,6 @@ import { dirname, join, relative } from "node:path";
|
|
|
15
15
|
import { stdout as output } from "node:process";
|
|
16
16
|
import { createInterface } from "node:readline/promises";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
|
-
import { parseDocument } from "yaml";
|
|
19
18
|
import {
|
|
20
19
|
REQUIRED_SELLABLE_MCP_TOOLS,
|
|
21
20
|
verifySellableMcpRuntime,
|
|
@@ -171,7 +170,7 @@ Commands:
|
|
|
171
170
|
uninstall Remove Sellable host config and installed artifacts.
|
|
172
171
|
|
|
173
172
|
Options:
|
|
174
|
-
--host <host> claude, codex,
|
|
173
|
+
--host <host> claude, codex, or all. Default: all
|
|
175
174
|
--server <mode> package, local, or hosted. Default: package
|
|
176
175
|
--token <token> Sellable API token. Also reads SELLABLE_TOKEN.
|
|
177
176
|
--workspace-id <id> Sellable workspace id. Also reads SELLABLE_WORKSPACE_ID.
|
|
@@ -191,9 +190,8 @@ Options:
|
|
|
191
190
|
Auth:
|
|
192
191
|
Install is auth-free by default. Sign in happens on the first run of
|
|
193
192
|
/sellable:create-campaign, /sellable:create-evergreen-campaigns,
|
|
194
|
-
/sellable:foundation, /sellable:content, /sellable:create-post,
|
|
195
|
-
/sellable:refresh-sender-engagement, or /sellable:refill-sends in Claude Code,
|
|
196
|
-
Codex, or Hermes,
|
|
193
|
+
/sellable:foundation, /sellable:content, /sellable:create-post, or
|
|
194
|
+
/sellable:refresh-sender-engagement, or /sellable:refill-sends in Claude Code or Codex,
|
|
197
195
|
where the agent walks you through signup or sign-in and stores credentials
|
|
198
196
|
in ~/.sellable/config.json.
|
|
199
197
|
|
|
@@ -205,7 +203,7 @@ Auth:
|
|
|
205
203
|
|
|
206
204
|
function printCreateCommandHint() {
|
|
207
205
|
printBanner();
|
|
208
|
-
console.log(` ${C.bold}Run Sellable from Claude Code
|
|
206
|
+
console.log(` ${C.bold}Run Sellable from Claude Code or Codex.${C.reset}`);
|
|
209
207
|
console.log("");
|
|
210
208
|
console.log(
|
|
211
209
|
` ${C.grey}The terminal command installs and verifies Sellable. Campaign creation runs inside your agent session so the workflow can use MCP tools, live state, and approval gates.${C.reset}`
|
|
@@ -239,18 +237,6 @@ function printCreateCommandHint() {
|
|
|
239
237
|
{ label: "Refill", command: "$sellable:refill-sends" },
|
|
240
238
|
]);
|
|
241
239
|
console.log("");
|
|
242
|
-
console.log("");
|
|
243
|
-
printAgentBox("Using Hermes?", "hermes", [
|
|
244
|
-
{ label: "Campaign", command: "/sellable-create-campaign" },
|
|
245
|
-
{ label: "A/B Test", command: "/sellable-create-ab-test" },
|
|
246
|
-
{ label: "Evergreen", command: "/sellable-create-evergreen-campaigns" },
|
|
247
|
-
{ label: "Foundation", command: "/sellable-foundation" },
|
|
248
|
-
{ label: "Content", command: "/sellable-content" },
|
|
249
|
-
{ label: "Post", command: "/sellable-create-post" },
|
|
250
|
-
{ label: "Refresh", command: "/sellable-refresh-sender-engagement" },
|
|
251
|
-
{ label: "Refill", command: "/sellable-refill-sends" },
|
|
252
|
-
]);
|
|
253
|
-
console.log("");
|
|
254
240
|
console.log(` ${"─".repeat(63)}`);
|
|
255
241
|
console.log(` ${C.grey}If those commands are missing, run:${C.reset}`);
|
|
256
242
|
console.log("");
|
|
@@ -263,10 +249,7 @@ function printCreateCommandHint() {
|
|
|
263
249
|
|
|
264
250
|
function parseArgs(argv) {
|
|
265
251
|
const opts = {
|
|
266
|
-
host:
|
|
267
|
-
process.env.SELLABLE_INSTALLER_HOST ||
|
|
268
|
-
process.env.SELLABLE_INSTALL_HOST ||
|
|
269
|
-
"all",
|
|
252
|
+
host: process.env.SELLABLE_INSTALL_HOST || "all",
|
|
270
253
|
server: process.env.SELLABLE_INSTALL_SERVER || "package",
|
|
271
254
|
token: process.env.SELLABLE_TOKEN || "",
|
|
272
255
|
workspaceId: process.env.SELLABLE_WORKSPACE_ID || "",
|
|
@@ -330,8 +313,8 @@ function parseArgs(argv) {
|
|
|
330
313
|
}
|
|
331
314
|
}
|
|
332
315
|
|
|
333
|
-
if (!["claude", "codex", "
|
|
334
|
-
throw new Error("--host must be claude, codex,
|
|
316
|
+
if (!["claude", "codex", "all"].includes(opts.host)) {
|
|
317
|
+
throw new Error("--host must be claude, codex, or all");
|
|
335
318
|
}
|
|
336
319
|
if (!["package", "local", "hosted"].includes(opts.server)) {
|
|
337
320
|
throw new Error("--server must be package, local, or hosted");
|
|
@@ -1192,29 +1175,6 @@ the Claude Code instruction for customer-facing language and host functions.
|
|
|
1192
1175
|
|
|
1193
1176
|
Do not tell Claude Code users to run \`$sellable:${commandName}\`, use
|
|
1194
1177
|
\`request_user_input\`, or restart Codex Desktop. Do not describe this run as Codex.`;
|
|
1195
|
-
}
|
|
1196
|
-
if (host === "hermes") {
|
|
1197
|
-
return `## Installed Host Contract
|
|
1198
|
-
|
|
1199
|
-
This installed skill is running in Hermes Agent. When the shared workflow body
|
|
1200
|
-
or fallback text mentions Claude Code, Codex, or Hermes for internal parity,
|
|
1201
|
-
choose the Hermes instruction for customer-facing language and host functions.
|
|
1202
|
-
|
|
1203
|
-
- Customer-facing command: \`/sellable-${commandName}\`
|
|
1204
|
-
- MCP tool naming: Hermes exposes Sellable tools as \`mcp_sellable_<tool>\`;
|
|
1205
|
-
when shared instructions show \`mcp__sellable__<tool>\`, call the matching
|
|
1206
|
-
\`mcp_sellable_<tool>\` tool instead.
|
|
1207
|
-
- Structured questions: ask plainly in chat unless a Hermes-native approval or
|
|
1208
|
-
question tool is visible in the current session.
|
|
1209
|
-
- Bootstrap host label: \`host: "Hermes"\`
|
|
1210
|
-
- Install/reload blocker label: Hermes install/reload problem
|
|
1211
|
-
- Reload instruction: restart Hermes, or run \`/reload-mcp\` in the active
|
|
1212
|
-
Hermes session after install
|
|
1213
|
-
|
|
1214
|
-
Do not tell Hermes users to run \`$sellable:${commandName}\` or
|
|
1215
|
-
\`/sellable:${commandName}\`, use \`AskUserQuestion\` or \`request_user_input\`,
|
|
1216
|
-
or restart Claude Code or Codex Desktop. Do not describe this run as Claude Code
|
|
1217
|
-
or Codex.`;
|
|
1218
1178
|
}
|
|
1219
1179
|
return "";
|
|
1220
1180
|
}
|
|
@@ -2624,256 +2584,6 @@ function codexPluginSkills() {
|
|
|
2624
2584
|
];
|
|
2625
2585
|
}
|
|
2626
2586
|
|
|
2627
|
-
function hermesCommandName(commandName) {
|
|
2628
|
-
return `sellable-${commandName}`;
|
|
2629
|
-
}
|
|
2630
|
-
|
|
2631
|
-
function normalizeHermesSkillMarkdown(markdown, commandName) {
|
|
2632
|
-
const hermesName = hermesCommandName(commandName);
|
|
2633
|
-
let content = String(markdown);
|
|
2634
|
-
if (/^name:\s*.+$/m.test(content)) {
|
|
2635
|
-
content = content.replace(/^name:\s*.+$/m, `name: ${hermesName}`);
|
|
2636
|
-
} else {
|
|
2637
|
-
content = content.replace(/^---\n/m, `---\nname: ${hermesName}\n`);
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
let normalized = content
|
|
2641
|
-
.replace(/mcp__sellable__<tool>/g, "mcp_sellable_<tool>")
|
|
2642
|
-
.replace(/mcp__([A-Za-z0-9_-]+)__<tool>/g, (_, server) => {
|
|
2643
|
-
return `mcp_${String(server).replaceAll("-", "_")}_<tool>`;
|
|
2644
|
-
})
|
|
2645
|
-
.replace(/\bmcp__([A-Za-z0-9_-]+)__([A-Za-z0-9_-]+)\b/g, (_, server, tool) => {
|
|
2646
|
-
return `mcp_${String(server).replaceAll("-", "_")}_${String(tool).replaceAll("-", "_")}`;
|
|
2647
|
-
})
|
|
2648
|
-
.replace(/\bmcp__sellable__/g, "mcp_sellable_")
|
|
2649
|
-
.replace(/\$sellable:/g, "/sellable-")
|
|
2650
|
-
.replace(/\/sellable:/g, "/sellable-")
|
|
2651
|
-
.replace(/Codex install\/reload problem/g, "Hermes install/reload problem")
|
|
2652
|
-
.replace(/Claude Code install\/reload problem/g, "Hermes install/reload problem")
|
|
2653
|
-
.replace(/fully quit and reopen Codex Desktop, then start a new thread/g, "restart Hermes, or run `/reload-mcp` in the active Hermes session")
|
|
2654
|
-
.replace(/fully quit and reopen Claude Code, then start a new session/g, "restart Hermes, or run `/reload-mcp` in the active Hermes session")
|
|
2655
|
-
.replace(/Codex Desktop/g, "Hermes")
|
|
2656
|
-
.replace(/Claude Code\/Codex/g, "Hermes")
|
|
2657
|
-
.replace(/Claude Code or Codex/g, "Hermes")
|
|
2658
|
-
.replace(/Codex or Claude Code/g, "Hermes")
|
|
2659
|
-
.replace(/Claude Code and Codex/g, "Hermes")
|
|
2660
|
-
.replace(/Codex\/Claude/g, "Hermes")
|
|
2661
|
-
.replace(/quick question panel/g, "approval prompt")
|
|
2662
|
-
.replace(/request_user_input/g, "plain chat")
|
|
2663
|
-
.replace(/AskUserQuestion/g, "plain chat");
|
|
2664
|
-
|
|
2665
|
-
normalized = normalized.replace(
|
|
2666
|
-
/Do not tell Hermes users to run[\s\S]*?Do not describe this run as Claude Code\nor Codex\./g,
|
|
2667
|
-
"Do not tell Hermes users to run Codex or Claude command forms, use Codex/Claude structured-question APIs, or restart Codex Desktop or Claude Code. Do not describe this run as Claude Code or Codex."
|
|
2668
|
-
);
|
|
2669
|
-
|
|
2670
|
-
normalized = normalized.replace(
|
|
2671
|
-
/## Structured Questions\n\nUse the host-native structured question gate for intake and approval:[\s\S]*?## Host Runtime Functions/g,
|
|
2672
|
-
`## Structured Questions
|
|
2673
|
-
|
|
2674
|
-
Hermes should ask setup questions directly in chat unless the current Hermes
|
|
2675
|
-
session exposes a native approval or question tool. Use a bounded approval gate
|
|
2676
|
-
only for explicit approval decisions. For open text like LinkedIn URLs, company
|
|
2677
|
-
domains, notes, pasted context, campaign ideas, or feedback, ask in normal chat
|
|
2678
|
-
and wait for the user to paste the value.
|
|
2679
|
-
|
|
2680
|
-
Campaign setup questions are single-choice decisions. Use mutually exclusive
|
|
2681
|
-
options and route blended or custom answers through a short free-text follow-up.
|
|
2682
|
-
Customer-facing language should call this "a couple setup choices" during
|
|
2683
|
-
normal campaign progress.
|
|
2684
|
-
|
|
2685
|
-
## Host Runtime Functions`
|
|
2686
|
-
);
|
|
2687
|
-
|
|
2688
|
-
normalized = normalized.replace(
|
|
2689
|
-
/## Names To Use\n\nUse these exact public names[\s\S]*?## Structured Questions/g,
|
|
2690
|
-
`## Names To Use
|
|
2691
|
-
|
|
2692
|
-
Use these exact public names for Hermes:
|
|
2693
|
-
|
|
2694
|
-
- Hermes command: \`/${hermesName}\`
|
|
2695
|
-
- Hermes skill directory: \`skills/sellable/${hermesName}/SKILL.md\`
|
|
2696
|
-
- MCP server name: \`sellable\`
|
|
2697
|
-
- Hermes MCP tool prefix: \`mcp_sellable_\`
|
|
2698
|
-
- Internal workflow prompt: \`create-campaign-v2\`
|
|
2699
|
-
|
|
2700
|
-
Do not tell users to run internal subskill names. \`create-campaign-v2\` is only
|
|
2701
|
-
the internal subskill loaded through
|
|
2702
|
-
\`mcp_sellable_get_subskill_prompt({ subskillName: "create-campaign-v2" })\`.
|
|
2703
|
-
|
|
2704
|
-
## Structured Questions`
|
|
2705
|
-
);
|
|
2706
|
-
|
|
2707
|
-
normalized = normalized.replace(
|
|
2708
|
-
/- `ask_user`:[\s\S]*?(?=\n- `load_subprompt`:)/g,
|
|
2709
|
-
`- \`ask_user\`: ask directly in chat unless a Hermes-native approval or
|
|
2710
|
-
question tool is visible in the current session. Use this for
|
|
2711
|
-
multiple-choice intake, campaign-focus choices, source decisions, and
|
|
2712
|
-
approvals. Campaign setup questions are single-choice only; do not use
|
|
2713
|
-
multi-select or checkbox variants.`
|
|
2714
|
-
);
|
|
2715
|
-
|
|
2716
|
-
normalized = normalized.replace(
|
|
2717
|
-
/- `launch_message_drafting`:[\s\S]*?\n\nIf a required interactive/g,
|
|
2718
|
-
`- \`launch_message_drafting\`: if Hermes exposes a background-agent capability,
|
|
2719
|
-
use the compatible Message Drafting agent. Otherwise run the same message
|
|
2720
|
-
drafting branch inline in the parent session with \`statusSource:
|
|
2721
|
-
"parent-thread-fallback"\`.
|
|
2722
|
-
|
|
2723
|
-
If a required interactive`
|
|
2724
|
-
);
|
|
2725
|
-
|
|
2726
|
-
normalized = normalized.replace(
|
|
2727
|
-
/Do not silently ask (?:Codex|Hermes) intake or approval questions as plain chat[\s\S]*?Plain chat questions are only acceptable in non-interactive `codex exec`\nsmoke\/rehearsal runs because structured user input is unavailable by design\nthere\.\n\n/g,
|
|
2728
|
-
""
|
|
2729
|
-
);
|
|
2730
|
-
|
|
2731
|
-
normalized = normalized.replace(
|
|
2732
|
-
/## Active Model Metadata\n\nBefore calling `bootstrap_create_campaign`, collect[\s\S]*?Never invent the model or reasoning effort\. Never pass config defaults as active\nmetadata\. If bootstrap returns `modelQuality\.status === "unknown"`, continue\nwithout asking the user to switch models\.\n\n/g,
|
|
2733
|
-
`## Active Model Metadata
|
|
2734
|
-
|
|
2735
|
-
Before calling \`bootstrap_create_campaign\`, pass \`host: "Hermes"\`. If the
|
|
2736
|
-
current Hermes session exposes model and effort metadata for this same turn,
|
|
2737
|
-
pass those exact values with \`modelMetadataSource: "hermes_runtime_metadata"\`.
|
|
2738
|
-
If the current Hermes session context explicitly states both the exact model ID
|
|
2739
|
-
and effort/thinking level, pass them with
|
|
2740
|
-
\`modelMetadataSource: "hermes_session_context"\`. Otherwise omit \`model\`,
|
|
2741
|
-
\`reasoningEffort\`, and \`modelMetadataSource\`.
|
|
2742
|
-
|
|
2743
|
-
Never invent the model or reasoning effort. Never inspect Codex or Claude config
|
|
2744
|
-
files as proof of the active Hermes session. If bootstrap returns
|
|
2745
|
-
\`modelQuality.status === "unknown"\`, continue without asking the user to
|
|
2746
|
-
switch models.
|
|
2747
|
-
|
|
2748
|
-
`
|
|
2749
|
-
);
|
|
2750
|
-
|
|
2751
|
-
normalized = normalized.replace(/not `plain chat`/g, "not internal API names");
|
|
2752
|
-
|
|
2753
|
-
normalized = normalized
|
|
2754
|
-
.replace(/this is a Codex\ninstall\/reload problem/g, "this is a Hermes\ninstall/reload problem")
|
|
2755
|
-
.replace(/Hermes plugin/g, "Hermes skills");
|
|
2756
|
-
|
|
2757
|
-
return normalized;
|
|
2758
|
-
}
|
|
2759
|
-
|
|
2760
|
-
function compactHermesEvergreenSkillMd() {
|
|
2761
|
-
return normalizeHermesSkillMarkdown(
|
|
2762
|
-
stampInstalledHost(`---
|
|
2763
|
-
name: create-evergreen-campaigns
|
|
2764
|
-
description: Create or repair evergreen campaign lanes without launching sends.
|
|
2765
|
-
visibility: public
|
|
2766
|
-
allowed-tools:
|
|
2767
|
-
${allowedToolsYaml(CREATE_EVERGREEN_CAMPAIGNS_ALLOWED_TOOLS)}
|
|
2768
|
-
---
|
|
2769
|
-
|
|
2770
|
-
# Sellable Create Evergreen Campaigns
|
|
2771
|
-
|
|
2772
|
-
Use this as the customer-facing entrypoint for the Sellable
|
|
2773
|
-
\`create-evergreen-campaigns\` workflow in Hermes.
|
|
2774
|
-
|
|
2775
|
-
## Bootstrap
|
|
2776
|
-
|
|
2777
|
-
MCP prompt and command access are required. First call
|
|
2778
|
-
\`mcp__sellable__get_auth_status({})\`. Do not inspect repo files, run shell
|
|
2779
|
-
commands, use \`npm\`, \`node\`, local harness scripts, or read local prompt
|
|
2780
|
-
files to emulate this workflow.
|
|
2781
|
-
|
|
2782
|
-
If the Sellable MCP prompt tools are unavailable, stop and say this is a Hermes
|
|
2783
|
-
install/reload problem. Tell the user to run
|
|
2784
|
-
\`curl -fsSL "https://app.sellable.dev/api/v2/cli/install" | sh\`, then restart
|
|
2785
|
-
Hermes or run \`/reload-mcp\`.
|
|
2786
|
-
|
|
2787
|
-
## Execute Workflow
|
|
2788
|
-
|
|
2789
|
-
1. Load the canonical prompt via
|
|
2790
|
-
\`mcp__sellable__get_subskill_prompt({ subskillName: "create-evergreen-campaigns" })\`.
|
|
2791
|
-
If the response has \`hasMore=true\`, continue with \`nextOffset\` until
|
|
2792
|
-
\`hasMore=false\`.
|
|
2793
|
-
2. Ask for bounded approval in normal chat before mutating if the plan requires
|
|
2794
|
-
approval. Do not call Codex app thread tools or Codex CLI workers from
|
|
2795
|
-
Hermes.
|
|
2796
|
-
3. Use \`mcp__sellable__setup_evergreen_campaigns({ mode: "plan" })\` for the
|
|
2797
|
-
plan/readback packet, then use the canonical prompt's approved mutation path
|
|
2798
|
-
and return to \`setup_evergreen_campaigns({ mode: "verify" })\` with
|
|
2799
|
-
customer-visible receipts.
|
|
2800
|
-
4. Do not launch campaigns, schedule sends, archive campaigns, delete tables,
|
|
2801
|
-
or spend paid credits.
|
|
2802
|
-
|
|
2803
|
-
## MCP Prompt Fallback
|
|
2804
|
-
|
|
2805
|
-
If exact subskill lookup fails, use
|
|
2806
|
-
\`mcp__sellable__search_subskill_prompts({ query: "create-evergreen-campaigns", includePublic: true, includeInternal: true })\`,
|
|
2807
|
-
then retry \`get_subskill_prompt\`.
|
|
2808
|
-
`, "hermes", "create-evergreen-campaigns"),
|
|
2809
|
-
"create-evergreen-campaigns"
|
|
2810
|
-
);
|
|
2811
|
-
}
|
|
2812
|
-
|
|
2813
|
-
function hermesSkillDefinitions() {
|
|
2814
|
-
return [
|
|
2815
|
-
{
|
|
2816
|
-
commandName: "create-campaign",
|
|
2817
|
-
dir: hermesCommandName("create-campaign"),
|
|
2818
|
-
skillMd: normalizeHermesSkillMarkdown(
|
|
2819
|
-
createCampaignSkillMd("hermes"),
|
|
2820
|
-
"create-campaign"
|
|
2821
|
-
),
|
|
2822
|
-
soulMd: createCampaignSoulMd(),
|
|
2823
|
-
},
|
|
2824
|
-
{
|
|
2825
|
-
commandName: "create-ab-test",
|
|
2826
|
-
dir: hermesCommandName("create-ab-test"),
|
|
2827
|
-
skillMd: normalizeHermesSkillMarkdown(
|
|
2828
|
-
createAbTestSkillMd("hermes"),
|
|
2829
|
-
"create-ab-test"
|
|
2830
|
-
),
|
|
2831
|
-
},
|
|
2832
|
-
{
|
|
2833
|
-
commandName: "create-evergreen-campaigns",
|
|
2834
|
-
dir: hermesCommandName("create-evergreen-campaigns"),
|
|
2835
|
-
skillMd: compactHermesEvergreenSkillMd(),
|
|
2836
|
-
},
|
|
2837
|
-
{
|
|
2838
|
-
commandName: "foundation",
|
|
2839
|
-
dir: hermesCommandName("foundation"),
|
|
2840
|
-
skillMd: normalizeHermesSkillMarkdown(
|
|
2841
|
-
foundationSkillMd("hermes"),
|
|
2842
|
-
"foundation"
|
|
2843
|
-
),
|
|
2844
|
-
},
|
|
2845
|
-
{
|
|
2846
|
-
commandName: "content",
|
|
2847
|
-
dir: hermesCommandName("content"),
|
|
2848
|
-
skillMd: normalizeHermesSkillMarkdown(contentSkillMd("hermes"), "content"),
|
|
2849
|
-
},
|
|
2850
|
-
{
|
|
2851
|
-
commandName: "create-post",
|
|
2852
|
-
dir: hermesCommandName("create-post"),
|
|
2853
|
-
skillMd: normalizeHermesSkillMarkdown(
|
|
2854
|
-
createPostSkillMd("hermes"),
|
|
2855
|
-
"create-post"
|
|
2856
|
-
),
|
|
2857
|
-
},
|
|
2858
|
-
{
|
|
2859
|
-
commandName: "refresh-sender-engagement",
|
|
2860
|
-
dir: hermesCommandName("refresh-sender-engagement"),
|
|
2861
|
-
skillMd: normalizeHermesSkillMarkdown(
|
|
2862
|
-
refreshSenderEngagementSkillMd("hermes"),
|
|
2863
|
-
"refresh-sender-engagement"
|
|
2864
|
-
),
|
|
2865
|
-
},
|
|
2866
|
-
{
|
|
2867
|
-
commandName: "refill-sends",
|
|
2868
|
-
dir: hermesCommandName("refill-sends"),
|
|
2869
|
-
skillMd: normalizeHermesSkillMarkdown(
|
|
2870
|
-
refillSendsSkillMd("hermes"),
|
|
2871
|
-
"refill-sends"
|
|
2872
|
-
),
|
|
2873
|
-
},
|
|
2874
|
-
];
|
|
2875
|
-
}
|
|
2876
|
-
|
|
2877
2587
|
function installerPackageRoot() {
|
|
2878
2588
|
return join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
2879
2589
|
}
|
|
@@ -3442,7 +3152,6 @@ exec ${shellQuote(npmCommand)} exec --yes --package ${shellQuote(INSTALL_PACKAGE
|
|
|
3442
3152
|
const WATCH_MODE_DRIVER_ENV = {
|
|
3443
3153
|
claude: "SELLABLE_WATCH_MODE_DRIVER=claude",
|
|
3444
3154
|
codex: "SELLABLE_WATCH_MODE_DRIVER=codex",
|
|
3445
|
-
hermes: "SELLABLE_WATCH_MODE_DRIVER=hermes",
|
|
3446
3155
|
};
|
|
3447
3156
|
|
|
3448
3157
|
function withHostedWatchModeDriver(rawUrl, driver) {
|
|
@@ -3568,169 +3277,6 @@ function codexPluginMcpServerMatches(content, opts) {
|
|
|
3568
3277
|
}
|
|
3569
3278
|
}
|
|
3570
3279
|
|
|
3571
|
-
function hermesHome() {
|
|
3572
|
-
return process.env.HERMES_HOME?.trim() || join(homedir(), ".hermes");
|
|
3573
|
-
}
|
|
3574
|
-
|
|
3575
|
-
function hermesConfigPath() {
|
|
3576
|
-
return join(hermesHome(), "config.yaml");
|
|
3577
|
-
}
|
|
3578
|
-
|
|
3579
|
-
function hermesSkillsRoot() {
|
|
3580
|
-
return join(hermesHome(), "skills", "sellable");
|
|
3581
|
-
}
|
|
3582
|
-
|
|
3583
|
-
function hermesLikelyInstalled() {
|
|
3584
|
-
return (
|
|
3585
|
-
Boolean(process.env.HERMES_HOME?.trim()) ||
|
|
3586
|
-
commandExists("hermes") ||
|
|
3587
|
-
existsSync(hermesConfigPath()) ||
|
|
3588
|
-
existsSync(join(hermesHome(), "skills"))
|
|
3589
|
-
);
|
|
3590
|
-
}
|
|
3591
|
-
|
|
3592
|
-
function parseHermesConfig(raw, configPath = hermesConfigPath()) {
|
|
3593
|
-
const source = raw.trim() ? raw : "{}\n";
|
|
3594
|
-
const doc = parseDocument(source, { prettyErrors: false });
|
|
3595
|
-
if (doc.errors.length > 0) {
|
|
3596
|
-
throw new Error(
|
|
3597
|
-
`Invalid Hermes config YAML at ${configPath}: ${doc.errors[0].message}`
|
|
3598
|
-
);
|
|
3599
|
-
}
|
|
3600
|
-
const value = doc.toJSON() ?? {};
|
|
3601
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3602
|
-
throw new Error(
|
|
3603
|
-
`Invalid Hermes config YAML at ${configPath}: expected a mapping at the document root`
|
|
3604
|
-
);
|
|
3605
|
-
}
|
|
3606
|
-
return value;
|
|
3607
|
-
}
|
|
3608
|
-
|
|
3609
|
-
function readHermesConfig() {
|
|
3610
|
-
const configPath = hermesConfigPath();
|
|
3611
|
-
const raw = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
|
|
3612
|
-
return { raw, config: parseHermesConfig(raw, configPath), configPath };
|
|
3613
|
-
}
|
|
3614
|
-
|
|
3615
|
-
function hermesMcpServer(opts) {
|
|
3616
|
-
if (opts.server === "hosted") {
|
|
3617
|
-
return {
|
|
3618
|
-
url: withHostedWatchModeDriver(opts.hostedUrl, "hermes"),
|
|
3619
|
-
enabled: true,
|
|
3620
|
-
env: {
|
|
3621
|
-
SELLABLE_WATCH_MODE_DRIVER: "hermes",
|
|
3622
|
-
},
|
|
3623
|
-
};
|
|
3624
|
-
}
|
|
3625
|
-
|
|
3626
|
-
const [command, args] = mcpCommand(opts);
|
|
3627
|
-
return {
|
|
3628
|
-
command,
|
|
3629
|
-
args,
|
|
3630
|
-
enabled: true,
|
|
3631
|
-
env: {
|
|
3632
|
-
SELLABLE_WATCH_MODE_DRIVER: "hermes",
|
|
3633
|
-
},
|
|
3634
|
-
};
|
|
3635
|
-
}
|
|
3636
|
-
|
|
3637
|
-
function writeHermesConfig(configPath, config, opts) {
|
|
3638
|
-
if (opts.dryRun) return;
|
|
3639
|
-
mkdirSync(dirname(configPath), { recursive: true, mode: 0o700 });
|
|
3640
|
-
const doc = parseDocument("{}\n");
|
|
3641
|
-
doc.contents = config;
|
|
3642
|
-
writeFileSync(configPath, String(doc), { mode: 0o600 });
|
|
3643
|
-
}
|
|
3644
|
-
|
|
3645
|
-
function writeHermesMcpServer(opts) {
|
|
3646
|
-
const { config, configPath } = readHermesConfig();
|
|
3647
|
-
const existingMcpServers =
|
|
3648
|
-
config.mcp_servers &&
|
|
3649
|
-
typeof config.mcp_servers === "object" &&
|
|
3650
|
-
!Array.isArray(config.mcp_servers)
|
|
3651
|
-
? config.mcp_servers
|
|
3652
|
-
: {};
|
|
3653
|
-
const nextConfig = {
|
|
3654
|
-
...config,
|
|
3655
|
-
mcp_servers: {
|
|
3656
|
-
...existingMcpServers,
|
|
3657
|
-
sellable: hermesMcpServer(opts),
|
|
3658
|
-
},
|
|
3659
|
-
};
|
|
3660
|
-
writeHermesConfig(configPath, nextConfig, opts);
|
|
3661
|
-
return configPath;
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
|
-
function installHermesSkills(opts) {
|
|
3665
|
-
const root = hermesSkillsRoot();
|
|
3666
|
-
for (const skill of hermesSkillDefinitions()) {
|
|
3667
|
-
const skillRoot = join(root, skill.dir);
|
|
3668
|
-
writeFile(join(skillRoot, "SKILL.md"), skill.skillMd, opts);
|
|
3669
|
-
if (skill.soulMd) {
|
|
3670
|
-
writeFile(join(skillRoot, "SOUL.md"), skill.soulMd, opts);
|
|
3671
|
-
}
|
|
3672
|
-
}
|
|
3673
|
-
return root;
|
|
3674
|
-
}
|
|
3675
|
-
|
|
3676
|
-
function installHermes(opts) {
|
|
3677
|
-
// Parse config before writing skills so invalid user YAML cannot leave a
|
|
3678
|
-
// half-installed Hermes skill tree behind.
|
|
3679
|
-
readHermesConfig();
|
|
3680
|
-
let skillsRoot = null;
|
|
3681
|
-
try {
|
|
3682
|
-
skillsRoot = installHermesSkills(opts);
|
|
3683
|
-
const configPath = writeHermesMcpServer(opts);
|
|
3684
|
-
logVerbose(`${C.grey}+ Hermes config updated: ${configPath}${C.reset}`);
|
|
3685
|
-
return { installed: true, configPath, skillsRoot };
|
|
3686
|
-
} catch (err) {
|
|
3687
|
-
if (skillsRoot && !opts.dryRun) {
|
|
3688
|
-
rmSync(skillsRoot, { recursive: true, force: true });
|
|
3689
|
-
}
|
|
3690
|
-
throw err;
|
|
3691
|
-
}
|
|
3692
|
-
}
|
|
3693
|
-
|
|
3694
|
-
function hermesMcpServerMatches(config, opts) {
|
|
3695
|
-
const server = config?.mcp_servers?.sellable;
|
|
3696
|
-
if (!server || typeof server !== "object") return false;
|
|
3697
|
-
const expected = hermesMcpServer(opts);
|
|
3698
|
-
return JSON.stringify(server) === JSON.stringify(expected);
|
|
3699
|
-
}
|
|
3700
|
-
|
|
3701
|
-
function hermesSkillChecks() {
|
|
3702
|
-
const root = hermesSkillsRoot();
|
|
3703
|
-
const skills = hermesSkillDefinitions();
|
|
3704
|
-
const paths = skills.map((skill) => join(root, skill.dir, "SKILL.md"));
|
|
3705
|
-
const allPresent = paths.every((skillPath) => existsSync(skillPath));
|
|
3706
|
-
const contents = paths
|
|
3707
|
-
.filter((skillPath) => existsSync(skillPath))
|
|
3708
|
-
.map((skillPath) => readFileSync(skillPath, "utf8"));
|
|
3709
|
-
const commandNamesCurrent =
|
|
3710
|
-
contents.length === skills.length &&
|
|
3711
|
-
skills.every((skill, index) =>
|
|
3712
|
-
contents[index]?.includes(`name: ${hermesCommandName(skill.commandName)}`)
|
|
3713
|
-
);
|
|
3714
|
-
const slashCommandsCurrent =
|
|
3715
|
-
contents.length === skills.length &&
|
|
3716
|
-
skills.every((skill, index) =>
|
|
3717
|
-
contents[index]?.includes(`/${hermesCommandName(skill.commandName)}`)
|
|
3718
|
-
);
|
|
3719
|
-
const forbiddenSyntax = contents.some((content) =>
|
|
3720
|
-
/\$sellable:|\/sellable:|mcp__sellable__|request_user_input|AskUserQuestion/.test(
|
|
3721
|
-
content
|
|
3722
|
-
)
|
|
3723
|
-
);
|
|
3724
|
-
return {
|
|
3725
|
-
root,
|
|
3726
|
-
paths,
|
|
3727
|
-
allPresent,
|
|
3728
|
-
commandNamesCurrent,
|
|
3729
|
-
slashCommandsCurrent,
|
|
3730
|
-
forbiddenSyntax,
|
|
3731
|
-
};
|
|
3732
|
-
}
|
|
3733
|
-
|
|
3734
3280
|
function installClaude(opts) {
|
|
3735
3281
|
if (!commandExists("claude")) {
|
|
3736
3282
|
const message =
|
|
@@ -4012,13 +3558,6 @@ function warningCheck(ok, label) {
|
|
|
4012
3558
|
return { ok, label, required: false };
|
|
4013
3559
|
}
|
|
4014
3560
|
|
|
4015
|
-
function firstRunCommandForHost(host) {
|
|
4016
|
-
if (host === "hermes") return "/sellable-create-campaign";
|
|
4017
|
-
if (host === "codex") return "$sellable:create-campaign";
|
|
4018
|
-
if (host === "claude") return "/sellable:create-campaign";
|
|
4019
|
-
return "/sellable:create-campaign, $sellable:create-campaign, or /sellable-create-campaign";
|
|
4020
|
-
}
|
|
4021
|
-
|
|
4022
3561
|
async function verify(opts) {
|
|
4023
3562
|
const startedAt = new Date().toISOString();
|
|
4024
3563
|
const stored = readStoredAuth();
|
|
@@ -4034,7 +3573,7 @@ async function verify(opts) {
|
|
|
4034
3573
|
checks.push(
|
|
4035
3574
|
warningCheck(
|
|
4036
3575
|
true,
|
|
4037
|
-
`Auth: not yet signed in — sign in on first run of
|
|
3576
|
+
`Auth: not yet signed in — sign in on first run of /sellable:create-campaign`
|
|
4038
3577
|
)
|
|
4039
3578
|
);
|
|
4040
3579
|
}
|
|
@@ -4321,88 +3860,6 @@ async function verify(opts) {
|
|
|
4321
3860
|
)
|
|
4322
3861
|
);
|
|
4323
3862
|
}
|
|
4324
|
-
if (opts.host === "hermes" || opts.host === "all") {
|
|
4325
|
-
let hermesConfig = null;
|
|
4326
|
-
let hermesConfigError = "";
|
|
4327
|
-
try {
|
|
4328
|
-
hermesConfig = readHermesConfig().config;
|
|
4329
|
-
} catch (err) {
|
|
4330
|
-
hermesConfigError = err instanceof Error ? err.message : String(err);
|
|
4331
|
-
}
|
|
4332
|
-
const npmPresent = opts.server !== "package" || commandExists(packageManagerCommand());
|
|
4333
|
-
const skillChecks = hermesSkillChecks();
|
|
4334
|
-
const configMatches = hermesConfig
|
|
4335
|
-
? hermesMcpServerMatches(hermesConfig, opts)
|
|
4336
|
-
: false;
|
|
4337
|
-
const hasHermesDriver =
|
|
4338
|
-
hermesConfig?.mcp_servers?.sellable?.env?.SELLABLE_WATCH_MODE_DRIVER ===
|
|
4339
|
-
"hermes";
|
|
4340
|
-
|
|
4341
|
-
const check =
|
|
4342
|
-
opts.host === "hermes" ? requiredCheck : warningCheck;
|
|
4343
|
-
checks.push(
|
|
4344
|
-
check(
|
|
4345
|
-
!hermesConfigError,
|
|
4346
|
-
hermesConfigError || `Hermes config readable: ${hermesConfigPath()}`
|
|
4347
|
-
)
|
|
4348
|
-
);
|
|
4349
|
-
checks.push(
|
|
4350
|
-
check(
|
|
4351
|
-
configMatches,
|
|
4352
|
-
configMatches
|
|
4353
|
-
? "Hermes Sellable MCP entry canonical"
|
|
4354
|
-
: "Hermes Sellable MCP entry missing or stale"
|
|
4355
|
-
)
|
|
4356
|
-
);
|
|
4357
|
-
checks.push(
|
|
4358
|
-
check(
|
|
4359
|
-
hasHermesDriver,
|
|
4360
|
-
hasHermesDriver
|
|
4361
|
-
? "Hermes watch mode driver pinned to hermes"
|
|
4362
|
-
: "Hermes watch mode driver missing"
|
|
4363
|
-
)
|
|
4364
|
-
);
|
|
4365
|
-
checks.push(
|
|
4366
|
-
check(
|
|
4367
|
-
skillChecks.allPresent,
|
|
4368
|
-
skillChecks.allPresent
|
|
4369
|
-
? "Hermes Sellable skills present"
|
|
4370
|
-
: "Hermes Sellable skills missing"
|
|
4371
|
-
)
|
|
4372
|
-
);
|
|
4373
|
-
checks.push(
|
|
4374
|
-
check(
|
|
4375
|
-
skillChecks.commandNamesCurrent,
|
|
4376
|
-
skillChecks.commandNamesCurrent
|
|
4377
|
-
? "Hermes skill names current"
|
|
4378
|
-
: "Hermes skill names stale"
|
|
4379
|
-
)
|
|
4380
|
-
);
|
|
4381
|
-
checks.push(
|
|
4382
|
-
check(
|
|
4383
|
-
skillChecks.slashCommandsCurrent,
|
|
4384
|
-
skillChecks.slashCommandsCurrent
|
|
4385
|
-
? "Hermes slash command hints current"
|
|
4386
|
-
: "Hermes slash command hints stale"
|
|
4387
|
-
)
|
|
4388
|
-
);
|
|
4389
|
-
checks.push(
|
|
4390
|
-
check(
|
|
4391
|
-
!skillChecks.forbiddenSyntax,
|
|
4392
|
-
!skillChecks.forbiddenSyntax
|
|
4393
|
-
? "Hermes skills use Hermes MCP/tool syntax"
|
|
4394
|
-
: "Hermes skills contain Claude/Codex-only syntax"
|
|
4395
|
-
)
|
|
4396
|
-
);
|
|
4397
|
-
checks.push(
|
|
4398
|
-
check(
|
|
4399
|
-
npmPresent,
|
|
4400
|
-
npmPresent
|
|
4401
|
-
? "Hermes package runtime command present"
|
|
4402
|
-
: `Hermes package runtime command missing: ${packageManagerCommand()}`
|
|
4403
|
-
)
|
|
4404
|
-
);
|
|
4405
|
-
}
|
|
4406
3863
|
|
|
4407
3864
|
let runtimeMcp;
|
|
4408
3865
|
try {
|
|
@@ -4585,7 +4042,7 @@ function printNextSteps(installedHosts, authReused) {
|
|
|
4585
4042
|
` ${C.bold}Almost there — install an agent first.${C.reset}`
|
|
4586
4043
|
);
|
|
4587
4044
|
console.log(
|
|
4588
|
-
` ${C.yellow}!${C.reset} No agent CLI
|
|
4045
|
+
` ${C.yellow}!${C.reset} No agent CLI found (Claude Code or Codex)`
|
|
4589
4046
|
);
|
|
4590
4047
|
console.log("");
|
|
4591
4048
|
console.log("");
|
|
@@ -4629,7 +4086,6 @@ function printNextSteps(installedHosts, authReused) {
|
|
|
4629
4086
|
|
|
4630
4087
|
const hasClaude = installedHosts.includes("Claude Code");
|
|
4631
4088
|
const hasCodex = installedHosts.includes("Codex");
|
|
4632
|
-
const hasHermes = installedHosts.includes("Hermes");
|
|
4633
4089
|
|
|
4634
4090
|
// Header
|
|
4635
4091
|
if (authReused) {
|
|
@@ -4645,11 +4101,6 @@ function printNextSteps(installedHosts, authReused) {
|
|
|
4645
4101
|
console.log(` ${C.green}✓${C.reset} Skills installed`);
|
|
4646
4102
|
console.log(` ${C.green}✓${C.reset} Codex custom agents installed`);
|
|
4647
4103
|
}
|
|
4648
|
-
if (hasHermes) {
|
|
4649
|
-
console.log(
|
|
4650
|
-
` ${C.green}✓${C.reset} Hermes skills and MCP config installed`
|
|
4651
|
-
);
|
|
4652
|
-
}
|
|
4653
4104
|
if (hasClaude) {
|
|
4654
4105
|
console.log(
|
|
4655
4106
|
` ${C.green}✓${C.reset} Claude slash commands and custom agents installed`
|
|
@@ -4693,19 +4144,6 @@ function printNextSteps(installedHosts, authReused) {
|
|
|
4693
4144
|
]);
|
|
4694
4145
|
console.log("");
|
|
4695
4146
|
}
|
|
4696
|
-
if (hasHermes) {
|
|
4697
|
-
printAgentBox("Using Hermes?", "hermes", [
|
|
4698
|
-
{ label: "Campaign", command: "/sellable-create-campaign" },
|
|
4699
|
-
{ label: "A/B Test", command: "/sellable-create-ab-test" },
|
|
4700
|
-
{ label: "Evergreen", command: "/sellable-create-evergreen-campaigns" },
|
|
4701
|
-
{ label: "Foundation", command: "/sellable-foundation" },
|
|
4702
|
-
{ label: "Content", command: "/sellable-content" },
|
|
4703
|
-
{ label: "Post", command: "/sellable-create-post" },
|
|
4704
|
-
{ label: "Refresh", command: "/sellable-refresh-sender-engagement" },
|
|
4705
|
-
{ label: "Refill", command: "/sellable-refill-sends" },
|
|
4706
|
-
]);
|
|
4707
|
-
console.log("");
|
|
4708
|
-
}
|
|
4709
4147
|
|
|
4710
4148
|
console.log("");
|
|
4711
4149
|
console.log(` ${"─".repeat(63)}`);
|
|
@@ -4735,7 +4173,7 @@ function removeTomlSection(content, header) {
|
|
|
4735
4173
|
return content.replace(pattern, "");
|
|
4736
4174
|
}
|
|
4737
4175
|
|
|
4738
|
-
function runUninstall(
|
|
4176
|
+
function runUninstall() {
|
|
4739
4177
|
console.log("");
|
|
4740
4178
|
console.log(` ${C.bold}Uninstalling Sellable…${C.reset}`);
|
|
4741
4179
|
console.log("");
|
|
@@ -4746,7 +4184,7 @@ function runUninstall(opts = { host: "all" }) {
|
|
|
4746
4184
|
|
|
4747
4185
|
// 1) Surgical removal from Codex config.toml
|
|
4748
4186
|
const codexConfigPath = join(codexHome(), "config.toml");
|
|
4749
|
-
if (
|
|
4187
|
+
if (existsSync(codexConfigPath)) {
|
|
4750
4188
|
try {
|
|
4751
4189
|
const before = readFileSync(codexConfigPath, "utf8");
|
|
4752
4190
|
const ts = new Date()
|
|
@@ -4783,11 +4221,10 @@ function runUninstall(opts = { host: "all" }) {
|
|
|
4783
4221
|
` ${C.yellow}!${C.reset} Could not edit Codex config: ${err instanceof Error ? err.message : String(err)}`
|
|
4784
4222
|
);
|
|
4785
4223
|
}
|
|
4786
|
-
} else
|
|
4224
|
+
} else {
|
|
4787
4225
|
skipped.push(`Codex config not found at ${codexConfigPath}`);
|
|
4788
4226
|
}
|
|
4789
4227
|
|
|
4790
|
-
if (opts.host === "codex" || opts.host === "all") {
|
|
4791
4228
|
for (const agent of [...codexCustomAgents(), ...legacyCodexCustomAgents()]) {
|
|
4792
4229
|
const agentPath = join(codexHome(), "agents", agent.filename);
|
|
4793
4230
|
if (!existsSync(agentPath)) continue;
|
|
@@ -4800,10 +4237,9 @@ function runUninstall(opts = { host: "all" }) {
|
|
|
4800
4237
|
);
|
|
4801
4238
|
}
|
|
4802
4239
|
}
|
|
4803
|
-
}
|
|
4804
4240
|
|
|
4805
4241
|
// 2) Claude Code MCP removal
|
|
4806
|
-
if (
|
|
4242
|
+
if (commandExists("claude")) {
|
|
4807
4243
|
const r = spawnSync("claude", ["mcp", "remove", "sellable"], {
|
|
4808
4244
|
encoding: "utf8",
|
|
4809
4245
|
});
|
|
@@ -4812,11 +4248,10 @@ function runUninstall(opts = { host: "all" }) {
|
|
|
4812
4248
|
} else {
|
|
4813
4249
|
skipped.push(`Claude Code MCP (none registered or already removed)`);
|
|
4814
4250
|
}
|
|
4815
|
-
} else
|
|
4251
|
+
} else {
|
|
4816
4252
|
skipped.push(`Claude Code CLI not found`);
|
|
4817
4253
|
}
|
|
4818
4254
|
|
|
4819
|
-
if (opts.host === "claude" || opts.host === "all") {
|
|
4820
4255
|
for (const agent of [
|
|
4821
4256
|
...claudeCustomAgents(),
|
|
4822
4257
|
...legacyClaudeCustomAgents(),
|
|
@@ -4844,66 +4279,10 @@ function runUninstall(opts = { host: "all" }) {
|
|
|
4844
4279
|
);
|
|
4845
4280
|
}
|
|
4846
4281
|
}
|
|
4847
|
-
}
|
|
4848
4282
|
|
|
4849
|
-
// 3)
|
|
4850
|
-
if (opts.host === "hermes" || opts.host === "all") {
|
|
4851
|
-
const configPath = hermesConfigPath();
|
|
4852
|
-
if (existsSync(configPath)) {
|
|
4853
|
-
try {
|
|
4854
|
-
const before = readFileSync(configPath, "utf8");
|
|
4855
|
-
const ts = new Date()
|
|
4856
|
-
.toISOString()
|
|
4857
|
-
.replace(/[-:]/g, "")
|
|
4858
|
-
.replace(/\..+/, "");
|
|
4859
|
-
const backupPath = `${configPath}.bak-sellable-uninstall-${ts}`;
|
|
4860
|
-
writeFileSync(backupPath, before, { mode: 0o600 });
|
|
4861
|
-
const config = parseHermesConfig(before, configPath);
|
|
4862
|
-
if (
|
|
4863
|
-
config.mcp_servers &&
|
|
4864
|
-
typeof config.mcp_servers === "object" &&
|
|
4865
|
-
!Array.isArray(config.mcp_servers) &&
|
|
4866
|
-
"sellable" in config.mcp_servers
|
|
4867
|
-
) {
|
|
4868
|
-
const nextMcpServers = { ...config.mcp_servers };
|
|
4869
|
-
delete nextMcpServers.sellable;
|
|
4870
|
-
writeHermesConfig(
|
|
4871
|
-
configPath,
|
|
4872
|
-
{ ...config, mcp_servers: nextMcpServers },
|
|
4873
|
-
{ dryRun: false }
|
|
4874
|
-
);
|
|
4875
|
-
removed.push(`Hermes config (sellable removed in ${configPath})`);
|
|
4876
|
-
removed.push(` backup: ${backupPath}`);
|
|
4877
|
-
} else {
|
|
4878
|
-
skipped.push(`Hermes config had no sellable MCP server`);
|
|
4879
|
-
}
|
|
4880
|
-
} catch (err) {
|
|
4881
|
-
console.log(
|
|
4882
|
-
` ${C.yellow}!${C.reset} Could not edit Hermes config: ${err instanceof Error ? err.message : String(err)}`
|
|
4883
|
-
);
|
|
4884
|
-
}
|
|
4885
|
-
} else {
|
|
4886
|
-
skipped.push(`Hermes config not found at ${configPath}`);
|
|
4887
|
-
}
|
|
4888
|
-
|
|
4889
|
-
const skillsRoot = hermesSkillsRoot();
|
|
4890
|
-
if (existsSync(skillsRoot)) {
|
|
4891
|
-
try {
|
|
4892
|
-
rmSync(skillsRoot, { recursive: true, force: true });
|
|
4893
|
-
removed.push(`${skillsRoot}`);
|
|
4894
|
-
} catch (err) {
|
|
4895
|
-
console.log(
|
|
4896
|
-
` ${C.yellow}!${C.reset} Could not remove ${skillsRoot}: ${err instanceof Error ? err.message : String(err)}`
|
|
4897
|
-
);
|
|
4898
|
-
}
|
|
4899
|
-
} else {
|
|
4900
|
-
skipped.push(`Hermes Sellable skills not found at ${skillsRoot}`);
|
|
4901
|
-
}
|
|
4902
|
-
}
|
|
4903
|
-
|
|
4904
|
-
// 4) Surgical removal of Sellable-installed artifacts inside ~/.sellable/
|
|
4283
|
+
// 3) Surgical removal of Sellable-installed artifacts inside ~/.sellable/
|
|
4905
4284
|
const sellableDir = join(homedir(), ".sellable");
|
|
4906
|
-
if (
|
|
4285
|
+
if (existsSync(sellableDir)) {
|
|
4907
4286
|
const tracked = [
|
|
4908
4287
|
"config.json",
|
|
4909
4288
|
"update-check.json",
|
|
@@ -4942,13 +4321,13 @@ function runUninstall(opts = { host: "all" }) {
|
|
|
4942
4321
|
// ignore
|
|
4943
4322
|
}
|
|
4944
4323
|
}
|
|
4945
|
-
} else
|
|
4324
|
+
} else {
|
|
4946
4325
|
skipped.push(`~/.sellable not present`);
|
|
4947
4326
|
}
|
|
4948
4327
|
|
|
4949
|
-
//
|
|
4328
|
+
// 4) Self-shim
|
|
4950
4329
|
const shimPath = join(homedir(), ".local", "bin", "sellable");
|
|
4951
|
-
if (
|
|
4330
|
+
if (existsSync(shimPath)) {
|
|
4952
4331
|
try {
|
|
4953
4332
|
rmSync(shimPath, { force: true });
|
|
4954
4333
|
removed.push(`${shimPath}`);
|
|
@@ -5061,13 +4440,6 @@ async function main() {
|
|
|
5061
4440
|
console.log(` Codex: $sellable:create-post`);
|
|
5062
4441
|
console.log(` Codex: $sellable:refresh-sender-engagement`);
|
|
5063
4442
|
console.log(` Codex: $sellable:refill-sends`);
|
|
5064
|
-
console.log(` Hermes: /sellable-create-campaign`);
|
|
5065
|
-
console.log(` Hermes: /sellable-create-evergreen-campaigns`);
|
|
5066
|
-
console.log(` Hermes: /sellable-foundation`);
|
|
5067
|
-
console.log(` Hermes: /sellable-content`);
|
|
5068
|
-
console.log(` Hermes: /sellable-create-post`);
|
|
5069
|
-
console.log(` Hermes: /sellable-refresh-sender-engagement`);
|
|
5070
|
-
console.log(` Hermes: /sellable-refill-sends`);
|
|
5071
4443
|
process.exit(0);
|
|
5072
4444
|
}
|
|
5073
4445
|
if (rawArgs[0] === "prefs") {
|
|
@@ -5110,8 +4482,7 @@ async function main() {
|
|
|
5110
4482
|
process.exit(0);
|
|
5111
4483
|
}
|
|
5112
4484
|
if (rawArgs[0] === "uninstall") {
|
|
5113
|
-
|
|
5114
|
-
runUninstall(opts);
|
|
4485
|
+
runUninstall();
|
|
5115
4486
|
process.exit(0);
|
|
5116
4487
|
}
|
|
5117
4488
|
if (rawArgs[0] === "create") {
|
|
@@ -5129,7 +4500,7 @@ async function main() {
|
|
|
5129
4500
|
if (!opts.verifyOnly) {
|
|
5130
4501
|
printBanner();
|
|
5131
4502
|
console.log(
|
|
5132
|
-
` ${C.bold}Connecting Sellable to Claude Code
|
|
4503
|
+
` ${C.bold}Connecting Sellable to Claude Code and Codex…${C.reset}`
|
|
5133
4504
|
);
|
|
5134
4505
|
console.log("");
|
|
5135
4506
|
} else if (!opts.json) {
|
|
@@ -5165,15 +4536,6 @@ async function main() {
|
|
|
5165
4536
|
installedHosts.push("Codex");
|
|
5166
4537
|
}
|
|
5167
4538
|
}
|
|
5168
|
-
if (
|
|
5169
|
-
opts.host === "hermes" ||
|
|
5170
|
-
(opts.host === "all" && hermesLikelyInstalled())
|
|
5171
|
-
) {
|
|
5172
|
-
const result = installHermes(opts);
|
|
5173
|
-
if (result.installed) {
|
|
5174
|
-
installedHosts.push("Hermes");
|
|
5175
|
-
}
|
|
5176
|
-
}
|
|
5177
4539
|
}
|
|
5178
4540
|
|
|
5179
4541
|
if (opts.dryRun) {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sellable/install",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.303",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "One-command installer for Sellable MCP in Claude Code
|
|
5
|
+
"description": "One-command installer for Sellable MCP in Claude Code and Codex",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sellable": "bin/sellable-install.mjs"
|
|
8
8
|
},
|
|
@@ -18,15 +18,13 @@
|
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@modelcontextprotocol/sdk": "^1.25.2"
|
|
22
|
-
"yaml": "^2.8.1"
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.25.2"
|
|
23
22
|
},
|
|
24
23
|
"keywords": [
|
|
25
24
|
"sellable",
|
|
26
25
|
"mcp",
|
|
27
26
|
"claude-code",
|
|
28
27
|
"codex",
|
|
29
|
-
"hermes",
|
|
30
28
|
"linkedin",
|
|
31
29
|
"outbound"
|
|
32
30
|
],
|