@blueprintit/shop-os-install 0.3.3 → 0.5.0

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
@@ -41,7 +41,7 @@ The installer (run directly or via the setup scripts above):
41
41
  5. **Vault**: creates a Shop OS vault folder (default `~/Shop OS Vault`) with a starter `CLAUDE.md`
42
42
  6. **Per-vault config**: writes `<vault>/.claude/settings.json` with `enabledPlugins` set for obsidian + superpowers
43
43
  7. **License record**: saves `~/.shopos/license.json` (chmod 600) for downstream skill validation
44
- 8. **Next steps**: prints `cd` command and the `/os-setup` slash command to run
44
+ 8. **Next steps**: prints `cd` command and the `/bp-setup` slash command to run
45
45
 
46
46
  Zero npm dependencies. Uses only Node 18+ built-ins (`fetch`, `readline`, `fs`).
47
47
 
@@ -25,6 +25,7 @@ import {
25
25
  existsSync,
26
26
  mkdirSync,
27
27
  readFileSync,
28
+ rmSync,
28
29
  writeFileSync,
29
30
  chmodSync,
30
31
  } from "node:fs";
@@ -138,7 +139,7 @@ async function validateLicense(key) {
138
139
  const url = `${LICENSE_SERVER}/validate?key=${encodeURIComponent(key)}`;
139
140
  let resp;
140
141
  try {
141
- resp = await fetch(url, { headers: { "user-agent": "shop-os-installer/0.3.0" } });
142
+ resp = await fetch(url, { headers: { "user-agent": "shop-os-installer/0.5.0" } });
142
143
  } catch (e) {
143
144
  return { ok: false, error: `network: ${e.message}` };
144
145
  }
@@ -172,49 +173,65 @@ function writeJSON(path, obj) {
172
173
  }
173
174
 
174
175
  function ensureMarketplaces(claudeRoot) {
176
+ // Always (re-)register the marketplaces and clear any stale clone on disk.
177
+ // A clone pinned to an old commit is the #1 reason re-installs keep serving
178
+ // old plugin versions (e.g. obsidian 3.8.0 skills appearing months after
179
+ // the marketplace shipped 3.12.0). Clearing the directory forces Claude Code
180
+ // to re-clone from origin on its next launch.
175
181
  const path = join(claudeRoot, "plugins", "known_marketplaces.json");
176
182
  const known = readJSON(path, {});
177
- const added = [];
183
+ const cleared = [];
178
184
  for (const mp of MARKETPLACES) {
179
- if (!known[mp.name]) {
180
- known[mp.name] = {
181
- source: { source: mp.source.type, repo: mp.source.repo },
182
- installLocation: join(claudeRoot, "plugins", "marketplaces", mp.name),
183
- lastUpdated: new Date().toISOString(),
184
- };
185
- added.push(mp.name);
185
+ const installLocation = join(claudeRoot, "plugins", "marketplaces", mp.name);
186
+ known[mp.name] = {
187
+ source: { source: mp.source.type, repo: mp.source.repo },
188
+ installLocation,
189
+ lastUpdated: new Date().toISOString(),
190
+ };
191
+ if (existsSync(installLocation)) {
192
+ try {
193
+ rmSync(installLocation, { recursive: true, force: true });
194
+ cleared.push(mp.name);
195
+ } catch {
196
+ // Best-effort. Locked files on Windows can prevent removal; Claude Code
197
+ // may keep using the stale clone in that case. Customer can manually
198
+ // delete the folder and re-launch to recover.
199
+ }
186
200
  }
187
201
  }
188
- if (added.length) writeJSON(path, known);
189
- return { added, total: MARKETPLACES.length };
202
+ writeJSON(path, known);
203
+ return { cleared, total: MARKETPLACES.length };
190
204
  }
191
205
 
192
206
  function ensurePluginsInstalled(claudeRoot) {
193
- // We record the plugins in installed_plugins.json. Claude Code's marketplace
194
- // refresh on next launch will fetch the actual plugin files into cache/.
195
- // If they're already present, we leave the entry alone (don't downgrade).
207
+ // Always reset the Shop OS-required plugin entries to a pending user-scope
208
+ // record. We can't use a presence check here: a previously-installed pinned
209
+ // entry (e.g. obsidian 3.8.0 from a prior beta) would prevent Claude Code
210
+ // from picking up the marketplace's current version. Forcing pending status
211
+ // makes Claude Code re-resolve the plugin against the (just-refreshed)
212
+ // marketplace clone on its next launch.
213
+ //
214
+ // This wipes any project-scope variants of these specific plugin ids — that's
215
+ // intentional. Shop OS is meant to be enabled at user scope so the same
216
+ // plugin set works across every vault the operator runs.
196
217
  const path = join(claudeRoot, "plugins", "installed_plugins.json");
197
218
  const existing = readJSON(path, { version: 2, plugins: {} });
198
219
  if (!existing.plugins) existing.plugins = {};
199
220
  const installedAt = new Date().toISOString();
200
- let changed = false;
201
221
  for (const id of PLUGINS_TO_ENABLE) {
202
- if (!existing.plugins[id]) {
203
- existing.plugins[id] = [
204
- {
205
- scope: "user",
206
- installPath: null, // filled in by Claude Code on next marketplace sync
207
- version: "pending",
208
- installedAt,
209
- lastUpdated: installedAt,
210
- gitCommitSha: "pending-sync",
211
- },
212
- ];
213
- changed = true;
214
- }
222
+ existing.plugins[id] = [
223
+ {
224
+ scope: "user",
225
+ installPath: null, // filled in by Claude Code on next marketplace sync
226
+ version: "pending",
227
+ installedAt,
228
+ lastUpdated: installedAt,
229
+ gitCommitSha: "pending-sync",
230
+ },
231
+ ];
215
232
  }
216
- if (changed) writeJSON(path, existing);
217
- return changed;
233
+ writeJSON(path, existing);
234
+ return PLUGINS_TO_ENABLE;
218
235
  }
219
236
 
220
237
  function enableForVault(vaultPath) {
@@ -233,6 +250,7 @@ function createVaultClaudeMd(vaultPath, license) {
233
250
  if (existsSync(claudeMd)) return false; // do not overwrite an existing vault
234
251
  const content = `---
235
252
  os-mode: business
253
+ bp-setup-state: pending
236
254
  license-customer: ${license.customer}
237
255
  license-product: ${license.product}
238
256
  installed-at: ${new Date().toISOString()}
@@ -244,7 +262,7 @@ Welcome to your Shop OS vault. This is the operating system Blueprint IT install
244
262
 
245
263
  To finish onboarding, run the following slash command inside Claude Code:
246
264
 
247
- \`/os-setup\`
265
+ \`/bp-setup\`
248
266
 
249
267
  This walks you through personalizing the vault for your shop: name, owner, key staff,
250
268
  services, daily routines, and more.
@@ -290,7 +308,7 @@ appropriate folder, and moves the original to \`Raw/processed/\` so the inbox st
290
308
  Open Claude Code in this vault and type the slash command:
291
309
 
292
310
  \`\`\`
293
- /os-digest
311
+ /bp-digest
294
312
  \`\`\`
295
313
 
296
314
  One command, easy to remember. Claude does the rest: reads each file, classifies it,
@@ -583,20 +601,15 @@ async function main() {
583
601
 
584
602
  print(dim(" [1/6] Registering plugin marketplaces"));
585
603
  const mpResult = ensureMarketplaces(claudeRoot);
586
- if (mpResult.added.length === 0) {
587
- info(`All ${mpResult.total} marketplaces already registered`);
588
- } else {
589
- for (const name of mpResult.added) ok(`Added marketplace: ${name}`);
604
+ for (const mp of MARKETPLACES) ok(`Marketplace ready: ${mp.name}`);
605
+ if (mpResult.cleared.length) {
606
+ info(`Cleared stale clone for ${mpResult.cleared.join(", ")} Claude Code will re-fetch on next launch.`);
590
607
  }
591
608
 
592
- print(dim(" [2/6] Enabling plugins for installation"));
593
- const pluginsChanged = ensurePluginsInstalled(claudeRoot);
594
- if (pluginsChanged) {
595
- for (const id of PLUGINS_TO_ENABLE) ok(`Queued plugin: ${id}`);
596
- info("Claude Code will sync the actual plugin files from the marketplaces on next launch.");
597
- } else {
598
- info("All required plugins already queued");
599
- }
609
+ print(dim(" [2/6] Queueing plugins for sync"));
610
+ const queued = ensurePluginsInstalled(claudeRoot);
611
+ for (const id of queued) ok(`Queued plugin: ${id}`);
612
+ info("Claude Code will sync the latest plugin files from the marketplaces on next launch.");
600
613
 
601
614
  print(dim(` [3/6] ${isExisting ? "Configuring" : "Creating"} vault at ${vaultPath}`));
602
615
  if (!existsSync(vaultPath)) {
@@ -634,7 +647,7 @@ async function main() {
634
647
  print(` 1. Open the ${cyan("Claude Code")} app you installed (Applications / Start menu)`);
635
648
  print(` 2. Pick this folder when it asks which to open:`);
636
649
  print(` ${cyan(vaultPath)}`);
637
- print(` 3. Type ${cyan("/os-setup")} to personalize your vault`);
650
+ print(` 3. Type ${cyan("/bp-setup")} to personalize your vault`);
638
651
  print(` 4. Walk through the onboarding interview`);
639
652
  print("");
640
653
  print(` 5. To let your team chat with the vault (read-only),`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueprintit/shop-os-install",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "description": "One-command installer for Shop OS — Blueprint IT's AI Operating System for small businesses.",
5
5
  "type": "module",
6
6
  "bin": {