@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 +1 -1
- package/bin/shop-os-install.js +58 -45
- package/package.json +1 -1
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 `/
|
|
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
|
|
package/bin/shop-os-install.js
CHANGED
|
@@ -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.
|
|
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
|
|
183
|
+
const cleared = [];
|
|
178
184
|
for (const mp of MARKETPLACES) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
189
|
-
return {
|
|
202
|
+
writeJSON(path, known);
|
|
203
|
+
return { cleared, total: MARKETPLACES.length };
|
|
190
204
|
}
|
|
191
205
|
|
|
192
206
|
function ensurePluginsInstalled(claudeRoot) {
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
217
|
-
return
|
|
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
|
-
\`/
|
|
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
|
-
/
|
|
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
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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]
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
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("/
|
|
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),`);
|