@sellable/install 0.1.804 → 0.1.806

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.
@@ -6,6 +6,7 @@ import {
6
6
  mkdirSync,
7
7
  readFileSync,
8
8
  renameSync,
9
+ rmdirSync,
9
10
  rmSync,
10
11
  writeFileSync,
11
12
  } from "node:fs";
@@ -35,6 +36,37 @@ const bundleDigest = createHash("sha256")
35
36
  .update(JSON.stringify(bundledFiles))
36
37
  .digest("hex");
37
38
 
39
+ // Skill files this image owned under an earlier name. A renamed skill must not
40
+ // linger beside its replacement, so each is removed when it is a plain file
41
+ // inside plain directories; anything else is left alone.
42
+ const retiredFiles = Object.freeze([
43
+ // Renamed to sellable-draft-message on 2026-10-05.
44
+ "skills/sellable/sellable-conversation-chess/SKILL.md",
45
+ ]);
46
+
47
+ function isPlainDirectory(path) {
48
+ const stat = lstatSync(path, { throwIfNoEntry: false });
49
+ return Boolean(stat?.isDirectory() && !stat.isSymbolicLink());
50
+ }
51
+
52
+ function removeRetiredFile(profileRoot, path) {
53
+ let directory = profileRoot;
54
+ for (const name of path.split("/").slice(0, -1)) {
55
+ directory = join(directory, name);
56
+ if (!isPlainDirectory(directory)) return false;
57
+ }
58
+ const target = join(profileRoot, path);
59
+ const stat = lstatSync(target, { throwIfNoEntry: false });
60
+ if (!stat?.isFile() || stat.isSymbolicLink()) return false;
61
+ rmSync(target);
62
+ try {
63
+ rmdirSync(directory);
64
+ } catch {
65
+ // The directory still holds something else; keep it.
66
+ }
67
+ return true;
68
+ }
69
+
38
70
  function assertDirectory(path) {
39
71
  const stat = lstatSync(path, { throwIfNoEntry: false });
40
72
  if (!stat) return;
@@ -80,6 +112,9 @@ export function reconcilePublicHermesSkills({
80
112
  }
81
113
  }
82
114
  let changed = false;
115
+ for (const path of retiredFiles) {
116
+ if (removeRetiredFile(profileRoot, path)) changed = true;
117
+ }
83
118
  for (const file of bundledFiles) {
84
119
  const target = join(profileRoot, file.path);
85
120
  if (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.804",
3
+ "version": "0.1.806",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {
@@ -216,8 +216,8 @@ preparation, the target is wrong, or status shows the wrong campaign/table, call
216
216
  `cancel_campaign_message_preparation`; otherwise do not cancel a healthy prepare
217
217
  run. Never call `start_campaign` from message preparation; final launch remains
218
218
  a separate explicit user greenlight.
219
- Use Template is the default message path; AI Generated is only an explicit
220
- opt-out.
219
+ First messages use Conversations or Message Testing; name the one you pick
220
+ as create-campaign-v2 `references/message-approach.md` says.
221
221
 
222
222
  ## DNC And Blocklist Handling
223
223
 
@@ -598,7 +598,7 @@ Drafting finishes or the recommendation is reviewed. If the user skips filters,
598
598
  first, then move to Messages/message review after it has started or returned a
599
599
  ready recommendation. `update_campaign({ currentStep: "messages" })` is not
600
600
  proof of kickoff. Enrichment/filtering and Generate Message cells wait for
601
- message approval. AI Generated is an explicit opt-out from the template path.
601
+ message approval (Message Testing only; Conversations has no template).
602
602
 
603
603
  The Message Drafting handoff must stay lean. Include only `campaignId`,
604
604
  `workflowTableId`, a concise brief summary, concise source summary/source-use
@@ -1,19 +1,20 @@
1
1
  ---
2
- name: conversation-chess
3
- description: Draft an opener, reply or follow-up from the current sender, offer and conversation. Start with the Sellable get_subskill_prompt tool for subskillName conversation-chess.
2
+ name: draft-message
3
+ description: Draft one opener, reply or follow-up as the current sender, from their offer and the conversation. Nothing is sent. Start with the Sellable get_subskill_prompt tool for subskillName draft-message.
4
4
  ---
5
5
 
6
- # Sellable Conversation Chess
6
+ # Sellable Draft Message
7
7
 
8
- Use this skill for one conversational draft. It does not send messages, schedule,
8
+ Use this skill for one message draft, in either campaign approach
9
+ (Conversations or Message Testing). It does not send messages, schedule,
9
10
  book, approve templates, enroll prospects or change stored sender context.
10
11
  Campaign creation and row persistence remain with their existing owners.
11
12
 
12
- 1. Load `mcp__sellable__get_subskill_prompt({ subskillName: "conversation-chess" })`.
13
+ 1. Load `mcp__sellable__get_subskill_prompt({ subskillName: "draft-message" })`.
13
14
  Read every page using its `nextOffset` until `hasMore` is false. Do not
14
15
  reconstruct the prompt from memory, a repository or a plugin-cache path.
15
16
  2. Load `core/input-contract.md` through
16
- `mcp__sellable__get_subskill_asset({ subskillName: "conversation-chess", assetPath: "..." })`.
17
+ `mcp__sellable__get_subskill_asset({ subskillName: "draft-message", assetPath: "..." })`.
17
18
  Then load the selected guide and examples from the root prompt:
18
19
  OPENER uses `modes/opener.md` + `references/openers.md` + `references/rapport.md` +
19
20
  `references/opener-editor.md`; REPLY uses
@@ -245,8 +245,8 @@ continue in the same conversation; do not ask the user to run another command.
245
245
  sending account: the latter belongs at Settings, not before the draft.
246
246
  The flow's order and state gates still hold: the campaign exists before
247
247
  anyone is imported, the source is chosen before the import, sample rows
248
- exist before filters and messages run, and the template is saved before
249
- anything is queued. Who decides each one is set by the next section. Offer
248
+ exist before filters and messages run, and in Message Testing the template
249
+ is saved before anything is queued. Who decides each one is set by the next section. Offer
250
250
  CRM or meeting integrations only as optional context when helpful.
251
251
  4. Reuse an existing draft for the same goal. Never create a duplicate because
252
252
  onboarding resumed in a new session.
@@ -304,11 +304,12 @@ for the message template: those are yours to decide and report.
304
304
  **How the first message is written.** The first campaign uses the
305
305
  Conversations approach from the workflow's `references/message-approach.md`:
306
306
  create it with `messageApproach: "conversation"`, write no message template,
307
- and let Sellable's writer write each first message for the person. Use Message
308
- Lab (`"message_test"`, one approved message to everyone) only when the
309
- customer asks to send the same message to everyone, to test copy, or to
310
- approve exact wording; then the template steps of the workflow apply and you
311
- approve the template yourself as before.
307
+ and let Sellable write each first message for the person. Use Message Testing
308
+ (`"message_test"`, one approved message to everyone) only when the customer
309
+ asks to send the same message to everyone, to test copy, or to approve exact
310
+ wording; then the template steps of the workflow apply and you approve the
311
+ template yourself as before. Name the approach to the customer with the
312
+ one-line descriptions in `references/message-approach.md`.
312
313
 
313
314
  **Approval 1: direction.** After research, write the brief, create the
314
315
  campaign with it (`create_campaign`), and post one message in this shape. The