@reservine/dx 1.0.0 → 1.1.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.
Files changed (2) hide show
  1. package/bin/cli.ts +145 -3
  2. package/package.json +1 -1
package/bin/cli.ts CHANGED
@@ -152,8 +152,113 @@ function install() {
152
152
  writeSettings(settings);
153
153
  ok(`Settings written to ${DIM}${SETTINGS_PATH}${RESET}`);
154
154
 
155
- // Step 3: Summary
156
- step(3, "Done!");
155
+ // Step 3: Clone marketplace + register in plugin system
156
+ step(3, "Installing marketplace plugin");
157
+
158
+ const PLUGINS_DIR = join(homedir(), ".claude", "plugins");
159
+ const KNOWN_MARKETPLACES_PATH = join(PLUGINS_DIR, "known_marketplaces.json");
160
+ const INSTALLED_PLUGINS_PATH = join(PLUGINS_DIR, "installed_plugins.json");
161
+ const CACHE_DIR = join(PLUGINS_DIR, "cache", "reservine-dx", "reservine-dx", "1.0.0");
162
+ const PLUGIN_SOURCE = join(MARKETPLACE_DIR, "plugins", "reservine-dx");
163
+
164
+ // 3a: Clone marketplace repo if not present
165
+ if (!existsSync(MARKETPLACE_DIR)) {
166
+ info("Cloning marketplace from GitHub...");
167
+ try {
168
+ execSync(
169
+ `git clone git@github.com:Reservine/Reservine-DX.git ${JSON.stringify(MARKETPLACE_DIR)} 2>&1`,
170
+ { encoding: "utf-8", stdio: "pipe" }
171
+ );
172
+ ok("Marketplace cloned");
173
+ } catch {
174
+ // Try HTTPS if SSH fails
175
+ try {
176
+ execSync(
177
+ `git clone ${REPO_URL} ${JSON.stringify(MARKETPLACE_DIR)} 2>&1`,
178
+ { encoding: "utf-8", stdio: "pipe" }
179
+ );
180
+ ok("Marketplace cloned (via HTTPS)");
181
+ } catch (e) {
182
+ warn("Failed to clone marketplace — Claude Code will clone on next startup");
183
+ }
184
+ }
185
+ } else {
186
+ info("Marketplace already cloned");
187
+ }
188
+
189
+ // 3b: Create cache symlink (Claude Code reads plugins from cache/)
190
+ if (existsSync(PLUGIN_SOURCE)) {
191
+ mkdirSync(join(PLUGINS_DIR, "cache", "reservine-dx", "reservine-dx"), { recursive: true });
192
+ if (existsSync(CACHE_DIR)) {
193
+ // Remove old cache entry (might be stale symlink)
194
+ try { execSync(`rm -rf ${JSON.stringify(CACHE_DIR)}`); } catch {}
195
+ }
196
+ try {
197
+ require("fs").symlinkSync(PLUGIN_SOURCE, CACHE_DIR);
198
+ ok("Cache symlink created");
199
+ } catch {
200
+ warn("Could not create cache symlink — Claude Code may not load skills until restart");
201
+ }
202
+ }
203
+
204
+ // 3c: Register in known_marketplaces.json
205
+ try {
206
+ const knownPath = KNOWN_MARKETPLACES_PATH;
207
+ const known = existsSync(knownPath)
208
+ ? JSON.parse(readFileSync(knownPath, "utf-8"))
209
+ : {};
210
+
211
+ if (!known["reservine-dx"]) {
212
+ known["reservine-dx"] = {
213
+ source: { source: "git", url: REPO_URL },
214
+ installLocation: MARKETPLACE_DIR,
215
+ lastUpdated: new Date().toISOString(),
216
+ };
217
+ writeFileSync(knownPath, JSON.stringify(known, null, 4) + "\n");
218
+ ok("Registered in known_marketplaces.json");
219
+ } else {
220
+ info("Already in known_marketplaces.json");
221
+ }
222
+ } catch {
223
+ warn("Could not update known_marketplaces.json");
224
+ }
225
+
226
+ // 3d: Register in installed_plugins.json
227
+ try {
228
+ const instPath = INSTALLED_PLUGINS_PATH;
229
+ const inst = existsSync(instPath)
230
+ ? JSON.parse(readFileSync(instPath, "utf-8"))
231
+ : { version: 2, plugins: {} };
232
+
233
+ if (!inst.plugins) inst.plugins = {};
234
+
235
+ // Get git commit sha if available
236
+ let sha = "";
237
+ try {
238
+ sha = execSync(`git -C ${JSON.stringify(MARKETPLACE_DIR)} rev-parse --short HEAD 2>/dev/null`, {
239
+ encoding: "utf-8",
240
+ }).trim();
241
+ } catch {}
242
+
243
+ inst.plugins["reservine-dx@reservine-dx"] = [
244
+ {
245
+ scope: "user",
246
+ installPath: CACHE_DIR,
247
+ version: "1.0.0",
248
+ installedAt: new Date().toISOString(),
249
+ lastUpdated: new Date().toISOString(),
250
+ gitCommitSha: sha,
251
+ },
252
+ ];
253
+
254
+ writeFileSync(instPath, JSON.stringify(inst, null, 4) + "\n");
255
+ ok("Registered in installed_plugins.json");
256
+ } catch {
257
+ warn("Could not update installed_plugins.json");
258
+ }
259
+
260
+ // Step 4: Summary
261
+ step(4, "Done!");
157
262
 
158
263
  console.log(`
159
264
  ${GREEN}${BOLD}Reservine-DX installed successfully!${RESET}
@@ -164,12 +269,13 @@ function install() {
164
269
  reservine-dx:cross-plan
165
270
  ${DIM}Commands${RESET} reservine-dx:cherry-pick-pr
166
271
  reservine-dx:commit
272
+ reservine-dx:cleanup
167
273
  ${DIM}Scripts${RESET} setup-worktree.sh (auto-detects Angular/Laravel)
168
274
 
169
275
  ${BOLD}Next steps:${RESET}
170
276
  ${DIM}1.${RESET} Restart Claude Code to load the marketplace
171
277
  ${DIM}2.${RESET} Run ${CYAN}/reservine-dx:new-feature-planning${RESET} in any Reservine repo
172
- ${DIM}3.${RESET} Run ${CYAN}bunx reservine-dx doctor${RESET} to verify everything
278
+ ${DIM}3.${RESET} Run ${CYAN}bunx @reservine/dx doctor${RESET} to verify everything
173
279
 
174
280
  ${DIM}Each repo needs a local plugin at:${RESET}
175
281
  ${DIM}.claude/skills/implement-plan/plugin.md${RESET}
@@ -485,6 +591,42 @@ function uninstall() {
485
591
  writeSettings(settings);
486
592
  ok(`Settings updated at ${DIM}${SETTINGS_PATH}${RESET}`);
487
593
 
594
+ // Remove from internal plugin registry
595
+ const PLUGINS_DIR = join(homedir(), ".claude", "plugins");
596
+
597
+ try {
598
+ const knownPath = join(PLUGINS_DIR, "known_marketplaces.json");
599
+ if (existsSync(knownPath)) {
600
+ const known = JSON.parse(readFileSync(knownPath, "utf-8"));
601
+ if (known["reservine-dx"]) {
602
+ delete known["reservine-dx"];
603
+ writeFileSync(knownPath, JSON.stringify(known, null, 4) + "\n");
604
+ ok("Removed from known_marketplaces.json");
605
+ }
606
+ }
607
+ } catch {}
608
+
609
+ try {
610
+ const instPath = join(PLUGINS_DIR, "installed_plugins.json");
611
+ if (existsSync(instPath)) {
612
+ const inst = JSON.parse(readFileSync(instPath, "utf-8"));
613
+ if (inst.plugins?.["reservine-dx@reservine-dx"]) {
614
+ delete inst.plugins["reservine-dx@reservine-dx"];
615
+ writeFileSync(instPath, JSON.stringify(inst, null, 4) + "\n");
616
+ ok("Removed from installed_plugins.json");
617
+ }
618
+ }
619
+ } catch {}
620
+
621
+ // Remove cache symlink
622
+ const cacheDir = join(PLUGINS_DIR, "cache", "reservine-dx");
623
+ if (existsSync(cacheDir)) {
624
+ try {
625
+ execSync(`rm -rf ${JSON.stringify(cacheDir)}`);
626
+ ok("Removed cache entry");
627
+ } catch {}
628
+ }
629
+
488
630
  console.log(`
489
631
  ${BOLD}Uninstalled.${RESET}
490
632
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reservine/dx",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Shared developer experience skills for the Reservine ecosystem",
5
5
  "type": "module",
6
6
  "bin": {