rush-ai 0.18.0 → 0.19.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/dist/index.js CHANGED
@@ -1,4 +1,38 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ backupCodexConfig,
4
+ codexConfigBackupPath,
5
+ codexConfigTomlPath,
6
+ codexHomeDir,
7
+ codexMarketplaceDir,
8
+ codexMarketplaceManifestPath,
9
+ codexMarketplacePluginDir,
10
+ codexPluginManifestPath,
11
+ codexPluginMcpPath,
12
+ codexPluginSkillsDir,
13
+ codexPluginVersionDir,
14
+ downgradeCodexCatalogEntryToStub,
15
+ getMcpServerOwner,
16
+ pluginSectionKey,
17
+ readCodexConfig,
18
+ removeMarketplaceSection,
19
+ removeMcpServerSection,
20
+ removePluginEntry,
21
+ restoreCodexConfigFromBackup,
22
+ setMarketplaceSection,
23
+ setMcpServerSection,
24
+ setPluginEntry,
25
+ syncCodexMarketplaceMirror,
26
+ writeCodexConfig,
27
+ writeCodexMarketplacePluginMirror
28
+ } from "./chunk-RLKEUPBP.js";
29
+ import {
30
+ SkillAuthError,
31
+ fetchRushMarketplace,
32
+ fetchRushPlugin,
33
+ materializeRushPlugin,
34
+ pickContentLoader
35
+ } from "./chunk-2AICQRQP.js";
2
36
  import {
3
37
  consumeSSEStreamWithReconnect
4
38
  } from "./chunk-MG4HY2PD.js";
@@ -15,9 +49,15 @@ import {
15
49
  RushError,
16
50
  TaskFailedError,
17
51
  VERSION,
52
+ createClient,
53
+ isRushError,
54
+ revokeCurrentSession,
55
+ setVerbosity,
56
+ verifyCurrentAuthSession
57
+ } from "./chunk-E6OSONYW.js";
58
+ import {
18
59
  checkAuthSourceMismatch,
19
60
  clearAuthConfig,
20
- createClient,
21
61
  createProfile,
22
62
  deleteProfile,
23
63
  getActiveProfile,
@@ -29,16 +69,19 @@ import {
29
69
  getProfileAuth,
30
70
  getProfileConfig,
31
71
  isLoggedIn,
32
- isRushError,
33
72
  listProfiles,
34
- output,
35
- revokeCurrentSession,
36
73
  setActiveProfile,
37
74
  setAuthConfig,
38
- setGlobalConfig,
39
- setVerbosity,
40
- verifyCurrentAuthSession
41
- } from "./chunk-YKZIRW26.js";
75
+ setGlobalConfig
76
+ } from "./chunk-OIKYNVKO.js";
77
+ import {
78
+ output
79
+ } from "./chunk-T5S6NCHZ.js";
80
+ import {
81
+ defaultRushBinaryResolver,
82
+ normalizeClaudeMcpServers,
83
+ readExternalMcpServers
84
+ } from "./chunk-X45FKY3L.js";
42
85
 
43
86
  // src/index.ts
44
87
  import chalk7 from "chalk";
@@ -403,7 +446,7 @@ function getApiBaseUrl() {
403
446
  async function loginViaBrowser(jsonMode) {
404
447
  const baseUrl = getApiBaseUrl();
405
448
  const state = randomBytes(16).toString("hex");
406
- return new Promise((resolve25, reject) => {
449
+ return new Promise((resolve23, reject) => {
407
450
  let authSaved = false;
408
451
  const rejectLogin = (error) => {
409
452
  clearAuthConfig();
@@ -506,7 +549,7 @@ async function loginViaBrowser(jsonMode) {
506
549
  );
507
550
  }
508
551
  }
509
- resolve25();
552
+ resolve23();
510
553
  } catch (err) {
511
554
  server.close();
512
555
  const authError = err instanceof AuthError ? err : new AuthError(
@@ -2385,24 +2428,24 @@ function registerDoctorCommand(program) {
2385
2428
  import { homedir as homedir5 } from "os";
2386
2429
 
2387
2430
  // src/installers/codex/installer.ts
2388
- import { constants as fsConstants3 } from "fs";
2431
+ import { constants as fsConstants } from "fs";
2389
2432
  import {
2390
- access as access3,
2391
- cp as cp2,
2392
- mkdir as mkdir4,
2393
- readdir as readdir2,
2394
- readFile as readFile4,
2395
- rm as rm3,
2396
- stat as stat3,
2397
- writeFile as writeFile4
2433
+ access,
2434
+ cp,
2435
+ mkdir as mkdir2,
2436
+ readdir,
2437
+ readFile as readFile2,
2438
+ rm,
2439
+ stat,
2440
+ writeFile as writeFile2
2398
2441
  } from "fs/promises";
2399
2442
  import { homedir as homedir3 } from "os";
2400
2443
  import {
2401
- basename as basename3,
2402
- dirname as dirname4,
2444
+ basename as basename2,
2445
+ dirname as dirname2,
2403
2446
  extname,
2404
2447
  relative as pathRelative,
2405
- resolve as resolve4
2448
+ resolve as resolve3
2406
2449
  } from "path";
2407
2450
 
2408
2451
  // src/installers/codex/mcp.ts
@@ -2485,1092 +2528,135 @@ function buildCodexMcpServerTomlEntry(config, options = {}) {
2485
2528
  return null;
2486
2529
  }
2487
2530
 
2488
- // src/installers/codex/mirror.ts
2489
- import { randomUUID as randomUUID2 } from "crypto";
2490
- import { constants as fsConstants2 } from "fs";
2491
- import {
2492
- access as access2,
2493
- cp,
2494
- mkdir as mkdir3,
2495
- mkdtemp,
2496
- readdir,
2497
- readFile as readFile3,
2498
- rename as rename2,
2499
- rm as rm2,
2500
- stat as stat2,
2501
- writeFile as writeFile3
2502
- } from "fs/promises";
2503
- import { basename as basename2, dirname as dirname3, resolve as resolve3 } from "path";
2504
-
2505
- // src/installers/codex/paths.ts
2506
- import { resolve as pathResolve } from "path";
2507
- function codexHomeDir(home) {
2508
- return pathResolve(home, ".codex");
2509
- }
2510
- function codexConfigTomlPath(home) {
2511
- return pathResolve(codexHomeDir(home), "config.toml");
2512
- }
2513
- function codexPluginsCacheDir(home) {
2514
- return pathResolve(codexHomeDir(home), "plugins", "cache");
2515
- }
2516
- function codexPluginsMarketplacesDir(home) {
2517
- return pathResolve(codexHomeDir(home), "plugins", "marketplaces");
2518
- }
2519
- function codexMarketplaceDir(home, marketplace) {
2520
- return pathResolve(codexPluginsMarketplacesDir(home), marketplace);
2521
- }
2522
- function codexMarketplaceManifestPath(marketplaceDir) {
2523
- return pathResolve(marketplaceDir, ".agents", "plugins", "marketplace.json");
2524
- }
2525
- function codexMarketplacePluginDir(marketplaceDir, pluginName) {
2526
- return pathResolve(marketplaceDir, "plugins", pluginName);
2527
- }
2528
- function codexPluginVersionDir(home, ref, version) {
2529
- return pathResolve(
2530
- codexPluginsCacheDir(home),
2531
- ref.marketplace,
2532
- ref.name,
2533
- version
2534
- );
2535
- }
2536
- function codexPluginManifestPath(versionDir) {
2537
- return pathResolve(versionDir, ".codex-plugin", "plugin.json");
2538
- }
2539
- function codexPluginMcpPath(versionDir) {
2540
- return pathResolve(versionDir, ".mcp.json");
2541
- }
2542
- function codexPluginSkillsDir(versionDir) {
2543
- return pathResolve(versionDir, "skills");
2544
- }
2545
- function codexConfigBackupPath(home, now) {
2546
- const ts = formatBackupTimestamp(now);
2547
- return `${codexConfigTomlPath(home)}.bak.${ts}`;
2548
- }
2549
- function formatBackupTimestamp(date) {
2550
- const pad = (n) => String(n).padStart(2, "0");
2551
- const yyyy = date.getUTCFullYear();
2552
- const mm = pad(date.getUTCMonth() + 1);
2553
- const dd = pad(date.getUTCDate());
2554
- const hh = pad(date.getUTCHours());
2555
- const mi = pad(date.getUTCMinutes());
2556
- const ss = pad(date.getUTCSeconds());
2557
- return `${yyyy}${mm}${dd}-${hh}${mi}${ss}`;
2558
- }
2559
- function pluginSectionKey(ref) {
2560
- return `${ref.name}@${ref.marketplace}`;
2561
- }
2562
-
2563
- // src/installers/codex/toml.ts
2564
- import { randomUUID } from "crypto";
2565
- import { constants as fsConstants } from "fs";
2566
- import {
2567
- access,
2568
- copyFile,
2569
- mkdir as mkdir2,
2570
- readFile as readFile2,
2571
- rename,
2572
- rm,
2573
- stat,
2574
- writeFile as writeFile2
2575
- } from "fs/promises";
2576
- import { dirname as dirname2 } from "path";
2577
- import tomlModule from "@iarna/toml";
2578
- var { parse: tomlParse, stringify: tomlStringify } = tomlModule;
2579
- var CodexConfigTomlError = class extends Error {
2531
+ // src/installers/codex/installer.ts
2532
+ var CODEX_UNSUPPORTED = ["rules", "hooks"];
2533
+ var CodexInstallError = class extends Error {
2580
2534
  constructor(message) {
2581
2535
  super(message);
2582
- this.name = "CodexConfigTomlError";
2583
- }
2584
- };
2585
- var CodexConfigTomlCorruptError = class extends CodexConfigTomlError {
2586
- constructor(filePath, cause) {
2587
- super(
2588
- `Codex config.toml \u89E3\u6790\u5931\u8D25\uFF08${filePath}\uFF09\u3002\u8BF7\u5907\u4EFD\u540E\u624B\u5DE5\u4FEE\u590D\u3002\u539F\u56E0\uFF1A${String(
2589
- cause?.message ?? cause
2590
- )}`
2591
- );
2592
- this.filePath = filePath;
2593
- this.cause = cause;
2594
- this.name = "CodexConfigTomlCorruptError";
2595
- }
2596
- };
2597
- var CodexConfigTomlConflictError = class extends CodexConfigTomlError {
2598
- constructor(filePath) {
2599
- super(
2600
- `Codex config.toml \u5E76\u53D1\u5199\u51B2\u7A81\uFF1A\u8BFB\u53D6\u548C\u5199\u5165\u4E4B\u95F4 ${filePath} \u88AB\u5176\u4ED6\u8FDB\u7A0B\u4FEE\u6539\u3002\u8BF7\u91CD\u8BD5\u3002`
2601
- );
2602
- this.filePath = filePath;
2603
- this.name = "CodexConfigTomlConflictError";
2536
+ this.name = "CodexInstallError";
2604
2537
  }
2605
2538
  };
2606
- async function readCodexConfig(filePath) {
2607
- if (!await pathExists2(filePath)) {
2608
- return { data: {}, mtimeMs: null };
2609
- }
2610
- const stats = await stat(filePath);
2611
- const raw = await readFile2(filePath, "utf8");
2612
- try {
2613
- const parsed = tomlParse(raw);
2614
- return { data: parsed, mtimeMs: stats.mtimeMs };
2615
- } catch (err) {
2616
- throw new CodexConfigTomlCorruptError(filePath, err);
2539
+ var CodexInstaller = class {
2540
+ target = "codex";
2541
+ home;
2542
+ rushAiBinaryResolver;
2543
+ now;
2544
+ marketplaceSource;
2545
+ constructor(opts = {}) {
2546
+ this.home = opts.home ?? homedir3();
2547
+ this.rushAiBinaryResolver = opts.rushAiBinaryResolver ?? defaultRushAiBinaryResolver;
2548
+ this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
2549
+ this.marketplaceSource = opts.marketplaceSource;
2617
2550
  }
2618
- }
2619
- async function backupCodexConfig(filePath, backupPath) {
2620
- if (!await pathExists2(filePath)) {
2621
- return null;
2551
+ // -------------------------------------------------------------------------
2552
+ // detect / isInstalled / list
2553
+ // -------------------------------------------------------------------------
2554
+ async detect() {
2555
+ return isDir(codexHomeDir(this.home));
2622
2556
  }
2623
- let finalBackup = backupPath;
2624
- if (await pathExists2(finalBackup)) {
2625
- finalBackup = `${backupPath}.${randomUUID().slice(0, 8)}`;
2557
+ async isInstalled(ref) {
2558
+ const cfgPath = codexConfigTomlPath(this.home);
2559
+ if (!await pathExists2(cfgPath)) return false;
2560
+ const { data } = await readCodexConfig(cfgPath);
2561
+ const plugins = data.plugins;
2562
+ if (!plugins || typeof plugins !== "object") return false;
2563
+ const key = pluginSectionKey(ref);
2564
+ const entry = plugins[key];
2565
+ if (!entry || typeof entry !== "object") return false;
2566
+ return entry.enabled === true;
2626
2567
  }
2627
- await copyFile(filePath, finalBackup);
2628
- return finalBackup;
2629
- }
2630
- async function writeCodexConfig(filePath, data, expectedMtimeMs) {
2631
- await assertNoMtimeDrift(filePath, expectedMtimeMs);
2632
- const serialized = tomlStringify(data);
2633
- await atomicWrite(filePath, serialized);
2634
- const afterStats = await stat(filePath);
2635
- return { mtimeMs: afterStats.mtimeMs };
2636
- }
2637
- async function restoreCodexConfigFromBackup(filePath, backupPath) {
2638
- if (!backupPath || !await pathExists2(backupPath)) {
2639
- if (await pathExists2(filePath)) {
2640
- await rm(filePath, { force: true });
2568
+ async list() {
2569
+ const cfgPath = codexConfigTomlPath(this.home);
2570
+ if (!await pathExists2(cfgPath)) return [];
2571
+ const { data } = await readCodexConfig(cfgPath);
2572
+ const plugins = data.plugins;
2573
+ if (!plugins || typeof plugins !== "object") return [];
2574
+ const results = [];
2575
+ for (const [key, raw] of Object.entries(plugins)) {
2576
+ if (!raw || typeof raw !== "object") continue;
2577
+ const entry = raw;
2578
+ if (entry.enabled !== true) continue;
2579
+ const ref = parsePluginKey(key);
2580
+ if (!ref) continue;
2581
+ const detected = await detectInstalledVersion(this.home, ref);
2582
+ results.push({
2583
+ ref,
2584
+ version: detected?.version ?? "unknown",
2585
+ installedAt: detected?.installedAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
2586
+ targets: ["codex"]
2587
+ });
2641
2588
  }
2642
- return;
2643
- }
2644
- const raw = await readFile2(backupPath, "utf8");
2645
- await atomicWrite(filePath, raw);
2646
- await rm(backupPath, { force: true }).catch(() => {
2647
- });
2648
- }
2649
- function setMarketplaceSection(config, name, entry) {
2650
- const mkts = ensureObjectSection(config, "marketplaces");
2651
- mkts[name] = { ...entry };
2652
- }
2653
- function removeMarketplaceSection(config, name) {
2654
- const mkts = config.marketplaces;
2655
- if (!mkts || typeof mkts !== "object") return;
2656
- delete mkts[name];
2657
- if (Object.keys(mkts).length === 0) {
2658
- delete config.marketplaces;
2659
- }
2660
- }
2661
- function setPluginEntry(config, key, entry) {
2662
- const plugins = ensureObjectSection(config, "plugins");
2663
- plugins[key] = { ...entry };
2664
- }
2665
- function removePluginEntry(config, key) {
2666
- const plugins = config.plugins;
2667
- if (!plugins || typeof plugins !== "object") return;
2668
- delete plugins[key];
2669
- if (Object.keys(plugins).length === 0) {
2670
- delete config.plugins;
2671
- }
2672
- }
2673
- var MCP_OWNER_MARKER_KEY = "_rush_ai_owner";
2674
- function setMcpServerSection(config, serverName, ownerKey, entry) {
2675
- const servers = ensureObjectSection(config, "mcp_servers");
2676
- servers[serverName] = { ...entry, [MCP_OWNER_MARKER_KEY]: ownerKey };
2677
- }
2678
- function removeMcpServerSection(config, serverName, expectedOwner) {
2679
- const servers = config.mcp_servers;
2680
- if (!servers || typeof servers !== "object") return "absent";
2681
- const entry = servers[serverName];
2682
- if (!entry || typeof entry !== "object") return "absent";
2683
- const owner = entry[MCP_OWNER_MARKER_KEY];
2684
- if (owner !== expectedOwner) return "foreign";
2685
- delete servers[serverName];
2686
- if (Object.keys(servers).length === 0) {
2687
- delete config.mcp_servers;
2688
- }
2689
- return "removed";
2690
- }
2691
- function getMcpServerOwner(config, serverName) {
2692
- const servers = config.mcp_servers;
2693
- if (!servers || typeof servers !== "object") return null;
2694
- const entry = servers[serverName];
2695
- if (!entry || typeof entry !== "object") return null;
2696
- const owner = entry[MCP_OWNER_MARKER_KEY];
2697
- return typeof owner === "string" ? owner : void 0;
2698
- }
2699
- async function pathExists2(p) {
2700
- try {
2701
- await access(p, fsConstants.F_OK);
2702
- return true;
2703
- } catch {
2704
- return false;
2705
- }
2706
- }
2707
- async function atomicWrite(filePath, content) {
2708
- await mkdir2(dirname2(filePath), { recursive: true });
2709
- const tmp = `${filePath}.${randomUUID()}.tmp`;
2710
- try {
2711
- await writeFile2(tmp, content, { encoding: "utf8", flag: "w" });
2712
- await rename(tmp, filePath);
2713
- } catch (err) {
2714
- await rm(tmp, { force: true }).catch(() => {
2589
+ results.sort((a, b) => {
2590
+ const ka = `${a.ref.name}@${a.ref.marketplace}`;
2591
+ const kb = `${b.ref.name}@${b.ref.marketplace}`;
2592
+ return ka < kb ? -1 : ka > kb ? 1 : 0;
2715
2593
  });
2716
- throw err;
2594
+ return results;
2717
2595
  }
2718
- }
2719
- async function assertNoMtimeDrift(filePath, expectedMtimeMs) {
2720
- if (expectedMtimeMs === null) {
2721
- if (await pathExists2(filePath)) {
2722
- throw new CodexConfigTomlConflictError(filePath);
2596
+ // -------------------------------------------------------------------------
2597
+ // install / uninstall
2598
+ // -------------------------------------------------------------------------
2599
+ async install(plugin, opts = {}) {
2600
+ if (!await this.detect()) {
2601
+ return {
2602
+ target: "codex",
2603
+ status: "skipped",
2604
+ included: [],
2605
+ skipped: [],
2606
+ artifacts: { files: [], mcpKeys: [] }
2607
+ };
2723
2608
  }
2724
- return;
2725
- }
2726
- const current = await stat(filePath).catch(() => null);
2727
- if (current === null) {
2728
- throw new CodexConfigTomlConflictError(filePath);
2729
- }
2730
- if (current.mtimeMs !== expectedMtimeMs) {
2731
- throw new CodexConfigTomlConflictError(filePath);
2732
- }
2733
- }
2734
- function ensureObjectSection(config, key) {
2735
- const existing = config[key];
2736
- if (existing === void 0) {
2737
- const fresh = {};
2738
- config[key] = fresh;
2739
- return fresh;
2740
- }
2741
- if (typeof existing !== "object" || existing === null || Array.isArray(existing)) {
2742
- throw new CodexConfigTomlError(
2743
- `config.toml \u9876\u5C42 '${key}' \u5DF2\u5B58\u5728\u4F46\u4E0D\u662F object\uFF08type=${Array.isArray(existing) ? "array" : typeof existing}\uFF09\u3002\u8BF7\u624B\u5DE5\u5907\u4EFD\u5E76\u6574\u7406\u8BE5\u5B57\u6BB5\u540E\u91CD\u8BD5\u3002`
2744
- );
2745
- }
2746
- return existing;
2747
- }
2748
-
2749
- // src/installers/codex/mirror.ts
2750
- function isSafePluginName(name) {
2751
- if (typeof name !== "string" || name.length === 0) return false;
2752
- if (name === "." || name === "..") return false;
2753
- if (name.startsWith(".")) return false;
2754
- if (name.includes("/") || name.includes("\\")) return false;
2755
- if (name.includes("..")) return false;
2756
- return /^[A-Za-z0-9_@][A-Za-z0-9._@-]*$/.test(name);
2757
- }
2758
- var DEFAULT_CATEGORY = "Engineering";
2759
- var DEFAULT_CAPABILITIES = ["Read", "Write"];
2760
- async function syncCodexMarketplaceMirror(home, resolved, opts = {}) {
2761
- const result = {
2762
- skipped: false,
2763
- added: [],
2764
- refreshed: [],
2765
- preservedFull: [],
2766
- removed: [],
2767
- warnings: []
2768
- };
2769
- if (!await isDir(codexHomeDir(home))) {
2770
- result.skipped = true;
2771
- return result;
2772
- }
2773
- const marketplaceDir = codexMarketplaceDir(home, resolved.name);
2774
- const manifestPath = codexMarketplaceManifestPath(marketplaceDir);
2775
- const contentLoader = opts.contentLoader ?? (async () => ({}));
2776
- const fallback = {
2777
- name: resolved.name,
2778
- interface: { displayName: resolved.name },
2779
- plugins: []
2780
- };
2781
- const existing = await readCodexMarketplaceCatalog(manifestPath, fallback);
2782
- const existingByName = new Map(existing.plugins.map((p) => [p.name, p]));
2783
- const remotePlugins = [];
2784
- const remoteNames = /* @__PURE__ */ new Set();
2785
- for (const entry of resolved.manifest.plugins) {
2786
- if (typeof entry?.name !== "string" || !isSafePluginName(entry.name)) {
2787
- const display = typeof entry?.name === "string" ? entry.name : "<unnamed>";
2788
- result.warnings.push(`skipped plugin with unsafe name: ${display}`);
2789
- output.warn(
2790
- `[codex] marketplace '${resolved.name}': skipped plugin with unsafe name: ${display}`
2791
- );
2792
- continue;
2609
+ const pathGuardError = assertSafePathComponents(plugin.ref, plugin.version);
2610
+ if (pathGuardError) {
2611
+ return {
2612
+ target: "codex",
2613
+ status: "failed",
2614
+ included: [],
2615
+ skipped: [],
2616
+ artifacts: { files: [], mcpKeys: [] },
2617
+ errors: [pathGuardError]
2618
+ };
2793
2619
  }
2794
- if (remoteNames.has(entry.name)) {
2795
- result.warnings.push(`duplicate plugin name in manifest: ${entry.name}`);
2796
- continue;
2620
+ const ref = plugin.ref;
2621
+ if (!opts.force && plugin.version !== "unknown" && await this.isInstalled(ref) && await isFullyInstalledMirror(this.home, ref)) {
2622
+ const existingVersion = await detectInstalledVersion(this.home, ref);
2623
+ if (existingVersion?.version === plugin.version) {
2624
+ return {
2625
+ target: "codex",
2626
+ status: "ok",
2627
+ included: computeIncludedCapabilities(plugin),
2628
+ skipped: [...CODEX_UNSUPPORTED],
2629
+ artifacts: { files: [], mcpKeys: [] },
2630
+ notes: [`\u63D2\u4EF6\u5DF2\u88C5\uFF08version=${plugin.version}\uFF09\uFF0C\u8DF3\u8FC7\u91CD\u590D\u5B89\u88C5`]
2631
+ };
2632
+ }
2797
2633
  }
2798
- remotePlugins.push(entry);
2799
- remoteNames.add(entry.name);
2800
- }
2801
- const nextEntries = [];
2802
- for (const entry of remotePlugins) {
2803
- const pluginDir = codexMarketplacePluginDir(marketplaceDir, entry.name);
2804
- const fullState = await detectFullPluginDir(pluginDir);
2805
- if (fullState === "full") {
2806
- const prior = existingByName.get(entry.name);
2807
- const base = prior ?? buildStubCatalogEntry(entry);
2808
- const fullEntry = {
2809
- ...base,
2810
- policy: { installation: "AVAILABLE", authentication: "ON_INSTALL" }
2811
- };
2812
- nextEntries.push(fullEntry);
2813
- result.preservedFull.push(entry.name);
2814
- continue;
2634
+ if (opts.dryRun) {
2635
+ return this.buildDryRunResult(plugin);
2815
2636
  }
2816
- let content = {};
2637
+ const rollback = {
2638
+ createdVersionDir: null,
2639
+ createdMarketplacePluginDir: null,
2640
+ backupPath: null,
2641
+ preExistingConfig: null
2642
+ };
2817
2643
  try {
2818
- content = await contentLoader(entry);
2644
+ return await this.doInstall(plugin, rollback);
2819
2645
  } catch (err) {
2820
- result.warnings.push(
2821
- `contentLoader failed for '${entry.name}': ${err.message}`
2822
- );
2823
- output.warn(
2824
- `[codex] marketplace '${resolved.name}': contentLoader failed for '${entry.name}': ${err.message}`
2825
- );
2826
- content = {};
2827
- }
2828
- try {
2829
- await writePluginMaterials(pluginDir, entry, content, resolved.name);
2830
- } catch (err) {
2831
- result.warnings.push(
2832
- `failed to write stub for '${entry.name}': ${err.message}`
2833
- );
2834
- output.warn(
2835
- `[codex] marketplace '${resolved.name}': failed to write stub for '${entry.name}': ${err.message}`
2836
- );
2837
- continue;
2838
- }
2839
- nextEntries.push(buildStubCatalogEntry(entry, content));
2840
- if (existingByName.has(entry.name)) {
2841
- result.refreshed.push(entry.name);
2842
- } else {
2843
- result.added.push(entry.name);
2844
- }
2845
- }
2846
- for (const prior of existing.plugins) {
2847
- if (remoteNames.has(prior.name)) continue;
2848
- if (!isSafePluginName(prior.name)) {
2849
- result.warnings.push(
2850
- `dropped catalog entry with unsafe name: ${prior.name}`
2851
- );
2852
- output.warn(
2853
- `[codex] marketplace '${resolved.name}': dropped catalog entry with unsafe name: ${prior.name}`
2854
- );
2855
- result.removed.push(prior.name);
2856
- continue;
2857
- }
2858
- const pluginDir = codexMarketplacePluginDir(marketplaceDir, prior.name);
2859
- if (await detectFullPluginDir(pluginDir) === "full") {
2860
- nextEntries.push(prior);
2861
- output.warn(
2862
- `[codex] marketplace '${resolved.name}': installed plugin '${prior.name}' no longer in remote catalog; keeping entry. Run 'rush-ai plugin uninstall ${prior.name}@${resolved.name}' if intended.`
2863
- );
2864
- continue;
2865
- }
2866
- await rm2(pluginDir, { recursive: true, force: true }).catch(() => {
2867
- });
2868
- result.removed.push(prior.name);
2869
- }
2870
- nextEntries.sort((a, b) => a.name.localeCompare(b.name));
2871
- const nextCatalog = {
2872
- name: existing.name || resolved.name,
2873
- interface: existing.interface ?? fallback.interface,
2874
- plugins: nextEntries
2875
- };
2876
- await writeFileAtomic(
2877
- manifestPath,
2878
- `${JSON.stringify(nextCatalog, null, 2)}
2879
- `
2880
- );
2881
- try {
2882
- await registerMarketplaceInCodexConfig({
2883
- home,
2884
- marketplaceName: resolved.name,
2885
- marketplaceDir,
2886
- now: opts.now ?? (() => /* @__PURE__ */ new Date())
2887
- });
2888
- } catch (err) {
2889
- const msg = err instanceof Error ? err.message : String(err);
2890
- result.warnings.push(
2891
- `failed to register [marketplaces.${resolved.name}] in config.toml: ${msg}`
2892
- );
2893
- output.warn(
2894
- `[codex] marketplace '${resolved.name}': failed to register in config.toml: ${msg}`
2895
- );
2896
- }
2897
- return result;
2898
- }
2899
- async function registerMarketplaceInCodexConfig(args) {
2900
- const cfgPath = codexConfigTomlPath(args.home);
2901
- await backupCodexConfig(
2902
- cfgPath,
2903
- codexConfigBackupPath(args.home, args.now())
2904
- );
2905
- const { data, mtimeMs } = await readCodexConfig(cfgPath);
2906
- setMarketplaceSection(data, args.marketplaceName, {
2907
- last_updated: args.now().toISOString(),
2908
- source_type: "local",
2909
- source: args.marketplaceDir
2910
- });
2911
- await writeCodexConfig(cfgPath, data, mtimeMs);
2912
- }
2913
- async function writeCodexMarketplacePluginMirror(input) {
2914
- const pluginParent = dirname3(input.pluginDir);
2915
- const pluginBase = basename2(input.pluginDir);
2916
- await mkdir3(pluginParent, { recursive: true });
2917
- const tmpPluginDir = await mkdtemp(
2918
- resolve3(pluginParent, `.${pluginBase}.tmp-`)
2919
- );
2920
- const backupPluginDir = await mkdtemp(
2921
- resolve3(pluginParent, `.${pluginBase}.bak-`)
2922
- );
2923
- let hadExistingPluginDir = false;
2924
- let swappedPluginDir = false;
2925
- try {
2926
- await rm2(tmpPluginDir, { recursive: true, force: true });
2927
- await rm2(backupPluginDir, { recursive: true, force: true }).catch(() => {
2928
- });
2929
- await cp(input.versionDir, tmpPluginDir, {
2930
- recursive: true,
2931
- dereference: false,
2932
- preserveTimestamps: true,
2933
- verbatimSymlinks: true
2934
- });
2935
- hadExistingPluginDir = await pathExists3(input.pluginDir);
2936
- if (hadExistingPluginDir) {
2937
- await rename2(input.pluginDir, backupPluginDir);
2938
- }
2939
- await rename2(tmpPluginDir, input.pluginDir);
2940
- swappedPluginDir = true;
2941
- await upsertCodexCatalogPluginFull(input.marketplaceDir, input.plugin);
2942
- if (hadExistingPluginDir) {
2943
- await rm2(backupPluginDir, { recursive: true, force: true });
2944
- }
2945
- } catch (err) {
2946
- await rm2(tmpPluginDir, { recursive: true, force: true }).catch(() => {
2947
- });
2948
- let restoredBackup = false;
2949
- if (hadExistingPluginDir && await pathExists3(backupPluginDir)) {
2950
- let displacedPluginDir = null;
2951
- try {
2952
- if (await pathExists3(input.pluginDir)) {
2953
- displacedPluginDir = await mkdtemp(
2954
- resolve3(pluginParent, `.${pluginBase}.failed-`)
2955
- );
2956
- await rm2(displacedPluginDir, { recursive: true, force: true });
2957
- await rename2(input.pluginDir, displacedPluginDir);
2958
- }
2959
- await rename2(backupPluginDir, input.pluginDir);
2960
- restoredBackup = true;
2961
- if (displacedPluginDir) {
2962
- await rm2(displacedPluginDir, { recursive: true, force: true }).catch(
2963
- () => {
2964
- }
2965
- );
2966
- }
2967
- } catch {
2968
- if (displacedPluginDir && !await pathExists3(input.pluginDir) && await pathExists3(displacedPluginDir)) {
2969
- await rename2(displacedPluginDir, input.pluginDir).catch(() => {
2970
- });
2971
- }
2972
- }
2973
- } else if (swappedPluginDir) {
2974
- await rm2(input.pluginDir, { recursive: true, force: true }).catch(
2975
- () => {
2976
- }
2977
- );
2978
- }
2979
- if (restoredBackup || !hadExistingPluginDir) {
2980
- await rm2(backupPluginDir, { recursive: true, force: true }).catch(
2981
- () => {
2982
- }
2983
- );
2984
- }
2985
- throw err;
2986
- }
2987
- }
2988
- async function upsertCodexCatalogPluginFull(marketplaceDir, plugin) {
2989
- const manifestPath = codexMarketplaceManifestPath(marketplaceDir);
2990
- const fallback = {
2991
- name: plugin.ref.marketplace,
2992
- interface: { displayName: plugin.ref.marketplace },
2993
- plugins: []
2994
- };
2995
- const catalog = await readCodexMarketplaceCatalog(manifestPath, fallback);
2996
- const nextEntry = {
2997
- name: plugin.ref.name,
2998
- source: { source: "local", path: `./plugins/${plugin.ref.name}` },
2999
- policy: { installation: "AVAILABLE", authentication: "ON_INSTALL" },
3000
- category: buildInterfaceCategory(plugin.manifest)
3001
- };
3002
- if (plugin.manifest.description !== void 0) {
3003
- nextEntry.description = plugin.manifest.description;
3004
- }
3005
- if (typeof plugin.manifest.version === "string") {
3006
- nextEntry.version = plugin.manifest.version;
3007
- }
3008
- const next = [
3009
- ...catalog.plugins.filter((entry) => entry.name !== plugin.ref.name),
3010
- nextEntry
3011
- ].sort((a, b) => a.name.localeCompare(b.name));
3012
- await writeFileAtomic(
3013
- manifestPath,
3014
- `${JSON.stringify({ ...catalog, plugins: next }, null, 2)}
3015
- `
3016
- );
3017
- }
3018
- async function downgradeCodexCatalogEntryToStub(marketplaceDir, pluginName) {
3019
- const manifestPath = codexMarketplaceManifestPath(marketplaceDir);
3020
- const pluginDir = codexMarketplacePluginDir(marketplaceDir, pluginName);
3021
- await rm2(pluginDir, { recursive: true, force: true }).catch(() => {
3022
- });
3023
- if (!await pathExists3(manifestPath)) return;
3024
- try {
3025
- const fallback = {
3026
- name: "",
3027
- interface: {},
3028
- plugins: []
3029
- };
3030
- const catalog = await readCodexMarketplaceCatalog(manifestPath, fallback);
3031
- const next = catalog.plugins.map((entry) => {
3032
- if (entry.name !== pluginName) return entry;
3033
- return {
3034
- ...entry,
3035
- policy: {
3036
- installation: "NOT_AVAILABLE",
3037
- authentication: "ON_INSTALL"
3038
- }
3039
- };
3040
- });
3041
- await writeFileAtomic(
3042
- manifestPath,
3043
- `${JSON.stringify({ ...catalog, plugins: next }, null, 2)}
3044
- `
3045
- );
3046
- } catch {
3047
- }
3048
- }
3049
- async function readCodexMarketplaceCatalog(manifestPath, fallback) {
3050
- if (!await pathExists3(manifestPath)) return fallback;
3051
- try {
3052
- const parsed = JSON.parse(await readFile3(manifestPath, "utf8"));
3053
- return {
3054
- name: typeof parsed.name === "string" ? parsed.name : fallback.name,
3055
- interface: parsed.interface && typeof parsed.interface === "object" && !Array.isArray(parsed.interface) ? parsed.interface : fallback.interface,
3056
- plugins: Array.isArray(parsed.plugins) ? parsed.plugins.filter(isCatalogPluginEntry) : fallback.plugins
3057
- };
3058
- } catch {
3059
- return fallback;
3060
- }
3061
- }
3062
- function isCatalogPluginEntry(value) {
3063
- return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.name === "string";
3064
- }
3065
- function buildStubCatalogEntry(entry, content = {}) {
3066
- const description = pickNonEmptyString(entry.description) ?? pickNonEmptyString(content.manifest?.description);
3067
- const version = pickNonEmptyString(entry.version) ?? pickNonEmptyString(content.manifest?.version);
3068
- const m = content.manifest ?? void 0;
3069
- const ifaceIn = m?.interface ?? {};
3070
- const entryCategory = pickNonEmptyString(
3071
- entry.category
3072
- );
3073
- const category = pickNonEmptyString(ifaceIn.category) ?? normalizeCategory(entryCategory) ?? DEFAULT_CATEGORY;
3074
- const out = {
3075
- name: entry.name,
3076
- source: { source: "local", path: `./plugins/${entry.name}` },
3077
- // stub 入口 → 标记为 NOT_AVAILABLE,让 Codex UI 禁用"连接"按钮,
3078
- // 因为点击连接只会写一份没有 MCP key 的伪安装。真正安装走
3079
- // `npx rush-ai@latest plugin install <name>@<marketplace>`。
3080
- // 已装 full 路径在 upsertCodexCatalogPluginFull 中覆写为 AVAILABLE。
3081
- policy: { installation: "NOT_AVAILABLE", authentication: "ON_INSTALL" },
3082
- category
3083
- };
3084
- if (description !== void 0) out.description = description;
3085
- if (version !== void 0) out.version = version;
3086
- return out;
3087
- }
3088
- function buildPluginJson(entry, content, marketplaceName) {
3089
- const m = content.manifest ?? void 0;
3090
- const author = m?.author ?? (entry.author && typeof entry.author === "object" ? entry.author : void 0);
3091
- const homepage = pickNonEmptyString(m?.homepage) ?? pickNonEmptyString(entry.homepage);
3092
- const license = pickNonEmptyString(m?.license) ?? pickNonEmptyString(entry.license);
3093
- const keywords = Array.isArray(m?.keywords) && m.keywords.length > 0 ? m.keywords : Array.isArray(entry.keywords) && entry.keywords.length > 0 ? entry.keywords : void 0;
3094
- const entryCategory = pickNonEmptyString(
3095
- entry.category
3096
- );
3097
- const sourceUrl = extractSourceUrl(entry.source);
3098
- const description = pickNonEmptyString(m?.description) ?? pickNonEmptyString(entry.description) ?? entry.name;
3099
- const version = pickNonEmptyString(m?.version) ?? pickNonEmptyString(entry.version) ?? "0.0.0";
3100
- const ifaceIn = m?.interface ?? {};
3101
- const developerName = pickNonEmptyString(ifaceIn.developerName) ?? pickNonEmptyString(author?.name);
3102
- const websiteURL = pickNonEmptyString(ifaceIn.websiteURL) ?? sourceUrl ?? homepage;
3103
- const category = pickNonEmptyString(ifaceIn.category) ?? normalizeCategory(entryCategory) ?? DEFAULT_CATEGORY;
3104
- const capabilities = Array.isArray(ifaceIn.capabilities) && ifaceIn.capabilities.length > 0 ? [...ifaceIn.capabilities] : [...DEFAULT_CAPABILITIES];
3105
- const installCmd = `npx rush-ai@latest plugin install ${entry.name}@${marketplaceName} --target codex`;
3106
- const shortHintPrefix = "\u26A0 \u9700\u5728\u7EC8\u7AEF\u5B89\u88C5 \uFF5C ";
3107
- const authorShort = pickNonEmptyString(ifaceIn.shortDescription) ?? description;
3108
- const baseLongDescription = pickNonEmptyString(ifaceIn.longDescription) ?? pickNonEmptyString(ifaceIn.shortDescription) ?? description;
3109
- const extraLinks = [];
3110
- if (homepage !== void 0 && homepage !== websiteURL) {
3111
- extraLinks.push(`\u5B98\u7F51\uFF1A${homepage}`);
3112
- }
3113
- if (sourceUrl !== void 0 && sourceUrl !== websiteURL && sourceUrl !== homepage) {
3114
- extraLinks.push(`\u6E90\u4EE3\u7801\uFF1A${sourceUrl}`);
3115
- }
3116
- const extraLinksSection = extraLinks.length > 0 ? `
3117
-
3118
- ${extraLinks.join("\n")}` : "";
3119
- const installSection = `\u2014\u2014\u2014
3120
- \u26A0 Codex \u684C\u9762\u7AEF\u7684"\u8FDE\u63A5"\u6309\u94AE\u65E0\u6CD5\u5B8C\u6574\u5B89\u88C5\u672C\u63D2\u4EF6
3121
- \u8BF7\u5728\u7EC8\u7AEF\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u5B8C\u6210\u5B89\u88C5\uFF1A
3122
- ${installCmd}`;
3123
- const longDescription = `${baseLongDescription}${extraLinksSection}
3124
-
3125
- ${installSection}`;
3126
- const defaultPrompt = Array.isArray(ifaceIn.defaultPrompt) ? [...ifaceIn.defaultPrompt] : [];
3127
- if (defaultPrompt.length === 0) {
3128
- defaultPrompt.push(installCmd);
3129
- }
3130
- const ifaceOut = {
3131
- displayName: pickNonEmptyString(ifaceIn.displayName) ?? entry.name,
3132
- shortDescription: `${shortHintPrefix}${authorShort}`,
3133
- longDescription,
3134
- category,
3135
- capabilities,
3136
- defaultPrompt
3137
- };
3138
- if (developerName !== void 0) ifaceOut.developerName = developerName;
3139
- if (websiteURL !== void 0) ifaceOut.websiteURL = websiteURL;
3140
- if (pickNonEmptyString(ifaceIn.privacyPolicyURL) !== void 0)
3141
- ifaceOut.privacyPolicyURL = ifaceIn.privacyPolicyURL;
3142
- if (pickNonEmptyString(ifaceIn.termsOfServiceURL) !== void 0)
3143
- ifaceOut.termsOfServiceURL = ifaceIn.termsOfServiceURL;
3144
- if (pickNonEmptyString(ifaceIn.brandColor) !== void 0)
3145
- ifaceOut.brandColor = ifaceIn.brandColor;
3146
- if (pickNonEmptyString(ifaceIn.composerIcon) !== void 0)
3147
- ifaceOut.composerIcon = ifaceIn.composerIcon;
3148
- if (pickNonEmptyString(ifaceIn.logo) !== void 0)
3149
- ifaceOut.logo = ifaceIn.logo;
3150
- if (Array.isArray(ifaceIn.screenshots) && ifaceIn.screenshots.length > 0)
3151
- ifaceOut.screenshots = [...ifaceIn.screenshots];
3152
- const out = {
3153
- // marker:让 detectFullPluginDir 可靠区分 sync vs install 写出的目录。
3154
- // Codex 不知道这个字段,会被忽略;marker 必须是合法 JSON 字段名才能被 plugin.json 接受。
3155
- [STUB_MARKER_KEY]: STUB_MARKER_VALUE,
3156
- name: entry.name,
3157
- version,
3158
- description,
3159
- skills: "./skills/",
3160
- interface: ifaceOut
3161
- };
3162
- if (author && typeof author === "object") {
3163
- out.author = { ...author };
3164
- }
3165
- if (homepage !== void 0) {
3166
- out.homepage = homepage;
3167
- }
3168
- if (sourceUrl !== void 0) {
3169
- out.repository = sourceUrl;
3170
- }
3171
- if (license !== void 0) {
3172
- out.license = license;
3173
- }
3174
- if (keywords !== void 0) {
3175
- out.keywords = [...keywords];
3176
- }
3177
- if (hasMcpServers(content)) {
3178
- out.mcpServers = "./.mcp.json";
3179
- }
3180
- return out;
3181
- }
3182
- function extractSourceUrl(source) {
3183
- if (!source || typeof source === "string") return void 0;
3184
- const obj = source;
3185
- const url = obj.url;
3186
- if (typeof url !== "string" || url.length === 0) return void 0;
3187
- if (!/^https?:\/\//i.test(url)) return void 0;
3188
- return url.replace(/\.git$/, "");
3189
- }
3190
- function normalizeCategory(raw) {
3191
- if (raw === void 0) return void 0;
3192
- const lower = raw.toLowerCase().trim();
3193
- const map = {
3194
- development: "Engineering",
3195
- data: "Engineering",
3196
- devops: "Engineering",
3197
- design: "Design",
3198
- productivity: "Productivity",
3199
- security: "Security",
3200
- research: "Research",
3201
- automation: "Productivity",
3202
- integration: "Engineering"
3203
- };
3204
- if (lower in map) return map[lower];
3205
- return lower.charAt(0).toUpperCase() + lower.slice(1);
3206
- }
3207
- async function writePluginMaterials(pluginDir, entry, content, marketplaceName) {
3208
- const manifestPath = codexPluginManifestPath(pluginDir);
3209
- await mkdir3(dirname3(manifestPath), { recursive: true });
3210
- await rm2(codexPluginMcpPath(pluginDir), { force: true }).catch(() => {
3211
- });
3212
- await rm2(codexPluginSkillsDir(pluginDir), {
3213
- recursive: true,
3214
- force: true
3215
- }).catch(() => {
3216
- });
3217
- const pluginJson = buildPluginJson(entry, content, marketplaceName);
3218
- await writeFileAtomic(
3219
- manifestPath,
3220
- `${JSON.stringify(pluginJson, null, 2)}
3221
- `
3222
- );
3223
- if (hasMcpServers(content)) {
3224
- const mcpPath = codexPluginMcpPath(pluginDir);
3225
- await writeFileAtomic(
3226
- mcpPath,
3227
- `${JSON.stringify({ mcpServers: content.mcpServers }, null, 2)}
3228
- `
3229
- );
3230
- }
3231
- if (content.skillsSourceDir && await isDir(content.skillsSourceDir)) {
3232
- const skillsDir = codexPluginSkillsDir(pluginDir);
3233
- await mkdir3(skillsDir, { recursive: true });
3234
- await cp(content.skillsSourceDir, skillsDir, {
3235
- recursive: true,
3236
- dereference: false,
3237
- preserveTimestamps: true,
3238
- verbatimSymlinks: true
3239
- });
3240
- } else if (Array.isArray(content.skillsPlaceholders) && content.skillsPlaceholders.length > 0) {
3241
- const installCmd = pickNonEmptyString(content.installCommand) ?? `npx rush-ai@latest plugin install ${entry.name}@${marketplaceName} --target codex`;
3242
- const pluginWebUrl = pickNonEmptyString(content.pluginWebUrl);
3243
- const skillsDir = codexPluginSkillsDir(pluginDir);
3244
- const usedDirs = /* @__PURE__ */ new Set();
3245
- for (const skill of content.skillsPlaceholders) {
3246
- if (!isSafeSkillName(skill.name)) continue;
3247
- const dirName = uniqueDirName(slugifySkillName(skill.name), usedDirs);
3248
- usedDirs.add(dirName);
3249
- const skillDir = resolve3(skillsDir, dirName);
3250
- await mkdir3(skillDir, { recursive: true });
3251
- const rawSkillMd = pickNonEmptyString(skill.rawSkillMd);
3252
- if (rawSkillMd !== void 0) {
3253
- const rewritten = rewriteSkillMdName(rawSkillMd, dirName);
3254
- await writeFileAtomic(resolve3(skillDir, "SKILL.md"), rewritten);
3255
- continue;
3256
- }
3257
- const desc = pickNonEmptyString(skill.description) ?? `Skill ${skill.name}`;
3258
- const fullDesc = skill.name === dirName ? desc : `${skill.name} \u2014 ${desc}`;
3259
- const skillUrl = pickNonEmptyString(skill.url);
3260
- const body = buildSkillPlaceholderBody({
3261
- name: skill.name,
3262
- description: fullDesc,
3263
- skillUrl,
3264
- pluginWebUrl,
3265
- installCmd
3266
- });
3267
- const md = `---
3268
- name: ${jsonQuote(dirName)}
3269
- description: ${jsonQuote(fullDesc)}
3270
- ---
3271
-
3272
- ${body}
3273
- `;
3274
- await writeFileAtomic(resolve3(skillDir, "SKILL.md"), md);
3275
- }
3276
- }
3277
- }
3278
- function rewriteSkillMdName(raw, dirName) {
3279
- const text = raw.replace(/\r\n/g, "\n");
3280
- if (!text.startsWith("---\n")) {
3281
- return `---
3282
- name: ${jsonQuote(dirName)}
3283
- description: ${jsonQuote(dirName)}
3284
- ---
3285
-
3286
- ` + text;
3287
- }
3288
- const lines = text.split("\n");
3289
- let closeIdx = -1;
3290
- for (let i = 1; i < lines.length; i++) {
3291
- if (lines[i] === "---") {
3292
- closeIdx = i;
3293
- break;
3294
- }
3295
- }
3296
- if (closeIdx === -1) {
3297
- return `---
3298
- name: ${jsonQuote(dirName)}
3299
- description: ${jsonQuote(dirName)}
3300
- ---
3301
-
3302
- ` + text;
3303
- }
3304
- let foundName = false;
3305
- for (let i = 1; i < closeIdx; i++) {
3306
- const line = lines[i] ?? "";
3307
- if (/^name\s*:/.test(line)) {
3308
- lines[i] = `name: ${jsonQuote(dirName)}`;
3309
- foundName = true;
3310
- break;
3311
- }
3312
- }
3313
- if (!foundName) {
3314
- lines.splice(1, 0, `name: ${jsonQuote(dirName)}`);
3315
- }
3316
- return lines.join("\n");
3317
- }
3318
- function buildSkillPlaceholderBody(input) {
3319
- const lines = [];
3320
- lines.push(`# ${input.name}`);
3321
- lines.push("");
3322
- lines.push(input.description);
3323
- lines.push("");
3324
- lines.push(
3325
- "> \u8FD9\u662F rush-ai marketplace \u540C\u6B65\u51FA\u7684\u5360\u4F4D skill \u2014\u2014 skill \u5B8C\u6574\u5185\u5BB9\uFF08trigger / \u64CD\u4F5C\u6B65\u9AA4 / \u63D0\u793A\u8BCD\uFF09\u5C1A\u672A\u4E0B\u8F7D\u3002"
3326
- );
3327
- lines.push(">");
3328
- lines.push("> \u5728\u7EC8\u7AEF\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u4EE5\u5B8C\u6574\u5B89\u88C5\uFF1A");
3329
- lines.push(">");
3330
- lines.push("> ```bash");
3331
- lines.push(`> ${input.installCmd}`);
3332
- lines.push("> ```");
3333
- if (input.skillUrl !== void 0) {
3334
- lines.push(">");
3335
- lines.push(`> \u4E5F\u53EF\u5728 web \u7AD9\u70B9\u67E5\u770B\uFF1A[${input.name}](${input.skillUrl})`);
3336
- }
3337
- if (input.pluginWebUrl !== void 0) {
3338
- lines.push(">");
3339
- lines.push(`> \u6D4F\u89C8\u6240\u5C5E plugin\uFF1A${input.pluginWebUrl}`);
3340
- }
3341
- return lines.join("\n");
3342
- }
3343
- function jsonQuote(s) {
3344
- return JSON.stringify(s);
3345
- }
3346
- function slugifySkillName(name) {
3347
- return name.replace(/^@/, "").replace(/[\\/.]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "") || "skill";
3348
- }
3349
- function uniqueDirName(base, used) {
3350
- if (!used.has(base)) return base;
3351
- let i = 2;
3352
- while (used.has(`${base}-${i}`)) i += 1;
3353
- return `${base}-${i}`;
3354
- }
3355
- function isSafeSkillName(name) {
3356
- if (typeof name !== "string" || name.length === 0) return false;
3357
- if (name === "." || name === "..") return false;
3358
- if (name.startsWith(".")) return false;
3359
- if (name.includes("\\")) return false;
3360
- if (name.includes("..")) return false;
3361
- const slashCount = (name.match(/\//g) ?? []).length;
3362
- if (slashCount > 1) return false;
3363
- if (slashCount === 1 && !name.startsWith("@")) return false;
3364
- return /^@?[A-Za-z0-9_][A-Za-z0-9._@/-]*$/.test(name);
3365
- }
3366
- function hasMcpServers(content) {
3367
- return !!content.mcpServers && typeof content.mcpServers === "object" && Object.keys(content.mcpServers).length > 0;
3368
- }
3369
- function pickNonEmptyString(value) {
3370
- if (typeof value === "string" && value.length > 0) return value;
3371
- return void 0;
3372
- }
3373
- var STUB_MARKER_KEY = "x-rush-mirror";
3374
- var STUB_MARKER_VALUE = "stub";
3375
- async function detectFullPluginDir(pluginDir) {
3376
- if (!await isDir(pluginDir)) return "stub";
3377
- const manifestPath = codexPluginManifestPath(pluginDir);
3378
- if (await pathExists3(manifestPath)) {
3379
- try {
3380
- const raw = await readFile3(manifestPath, "utf8");
3381
- const parsed = JSON.parse(raw);
3382
- if (parsed[STUB_MARKER_KEY] === STUB_MARKER_VALUE) {
3383
- return "stub";
3384
- }
3385
- } catch {
3386
- }
3387
- }
3388
- if (await dirHasEntries(codexPluginSkillsDir(pluginDir))) {
3389
- return "full";
3390
- }
3391
- const mcpPath = codexPluginMcpPath(pluginDir);
3392
- if (await pathExists3(mcpPath)) {
3393
- try {
3394
- const raw = await readFile3(mcpPath, "utf8");
3395
- if (!/\$\{[^}]+\}/.test(raw)) {
3396
- return "full";
3397
- }
3398
- } catch {
3399
- }
3400
- }
3401
- return "stub";
3402
- }
3403
- async function dirHasEntries(dir) {
3404
- if (!await isDir(dir)) return false;
3405
- let entries;
3406
- try {
3407
- entries = await readdir(dir, { withFileTypes: true });
3408
- } catch {
3409
- return false;
3410
- }
3411
- return entries.length > 0;
3412
- }
3413
- function buildInterfaceCategory(manifest) {
3414
- return manifest.interface?.category ?? DEFAULT_CATEGORY;
3415
- }
3416
- async function writeFileAtomic(filePath, content) {
3417
- await mkdir3(dirname3(filePath), { recursive: true });
3418
- const tmp = `${filePath}.${randomUUID2()}.tmp`;
3419
- try {
3420
- await writeFile3(tmp, content, { encoding: "utf8", flag: "w" });
3421
- await rename2(tmp, filePath);
3422
- } catch (err) {
3423
- await rm2(tmp, { force: true }).catch(() => {
3424
- });
3425
- throw err;
3426
- }
3427
- }
3428
- async function pathExists3(p) {
3429
- try {
3430
- await access2(p, fsConstants2.F_OK);
3431
- return true;
3432
- } catch {
3433
- return false;
3434
- }
3435
- }
3436
- async function isDir(p) {
3437
- try {
3438
- const s = await stat2(p);
3439
- return s.isDirectory();
3440
- } catch {
3441
- return false;
3442
- }
3443
- }
3444
-
3445
- // src/installers/codex/installer.ts
3446
- var CODEX_UNSUPPORTED = ["rules", "hooks"];
3447
- var CodexInstallError = class extends Error {
3448
- constructor(message) {
3449
- super(message);
3450
- this.name = "CodexInstallError";
3451
- }
3452
- };
3453
- var CodexInstaller = class {
3454
- target = "codex";
3455
- home;
3456
- rushAiBinaryResolver;
3457
- now;
3458
- marketplaceSource;
3459
- constructor(opts = {}) {
3460
- this.home = opts.home ?? homedir3();
3461
- this.rushAiBinaryResolver = opts.rushAiBinaryResolver ?? defaultRushAiBinaryResolver;
3462
- this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
3463
- this.marketplaceSource = opts.marketplaceSource;
3464
- }
3465
- // -------------------------------------------------------------------------
3466
- // detect / isInstalled / list
3467
- // -------------------------------------------------------------------------
3468
- async detect() {
3469
- return isDir2(codexHomeDir(this.home));
3470
- }
3471
- async isInstalled(ref) {
3472
- const cfgPath = codexConfigTomlPath(this.home);
3473
- if (!await pathExists4(cfgPath)) return false;
3474
- const { data } = await readCodexConfig(cfgPath);
3475
- const plugins = data.plugins;
3476
- if (!plugins || typeof plugins !== "object") return false;
3477
- const key = pluginSectionKey(ref);
3478
- const entry = plugins[key];
3479
- if (!entry || typeof entry !== "object") return false;
3480
- return entry.enabled === true;
3481
- }
3482
- async list() {
3483
- const cfgPath = codexConfigTomlPath(this.home);
3484
- if (!await pathExists4(cfgPath)) return [];
3485
- const { data } = await readCodexConfig(cfgPath);
3486
- const plugins = data.plugins;
3487
- if (!plugins || typeof plugins !== "object") return [];
3488
- const results = [];
3489
- for (const [key, raw] of Object.entries(plugins)) {
3490
- if (!raw || typeof raw !== "object") continue;
3491
- const entry = raw;
3492
- if (entry.enabled !== true) continue;
3493
- const ref = parsePluginKey(key);
3494
- if (!ref) continue;
3495
- const detected = await detectInstalledVersion(this.home, ref);
3496
- results.push({
3497
- ref,
3498
- version: detected?.version ?? "unknown",
3499
- installedAt: detected?.installedAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
3500
- targets: ["codex"]
3501
- });
3502
- }
3503
- results.sort((a, b) => {
3504
- const ka = `${a.ref.name}@${a.ref.marketplace}`;
3505
- const kb = `${b.ref.name}@${b.ref.marketplace}`;
3506
- return ka < kb ? -1 : ka > kb ? 1 : 0;
3507
- });
3508
- return results;
3509
- }
3510
- // -------------------------------------------------------------------------
3511
- // install / uninstall
3512
- // -------------------------------------------------------------------------
3513
- async install(plugin, opts = {}) {
3514
- if (!await this.detect()) {
3515
- return {
3516
- target: "codex",
3517
- status: "skipped",
3518
- included: [],
3519
- skipped: [],
3520
- artifacts: { files: [], mcpKeys: [] }
3521
- };
3522
- }
3523
- const pathGuardError = assertSafePathComponents(plugin.ref, plugin.version);
3524
- if (pathGuardError) {
3525
- return {
3526
- target: "codex",
3527
- status: "failed",
3528
- included: [],
3529
- skipped: [],
3530
- artifacts: { files: [], mcpKeys: [] },
3531
- errors: [pathGuardError]
3532
- };
3533
- }
3534
- const ref = plugin.ref;
3535
- if (!opts.force && plugin.version !== "unknown" && await this.isInstalled(ref) && await isFullyInstalledMirror(this.home, ref)) {
3536
- const existingVersion = await detectInstalledVersion(this.home, ref);
3537
- if (existingVersion?.version === plugin.version) {
3538
- return {
3539
- target: "codex",
3540
- status: "ok",
3541
- included: computeIncludedCapabilities(plugin),
3542
- skipped: [...CODEX_UNSUPPORTED],
3543
- artifacts: { files: [], mcpKeys: [] },
3544
- notes: [`\u63D2\u4EF6\u5DF2\u88C5\uFF08version=${plugin.version}\uFF09\uFF0C\u8DF3\u8FC7\u91CD\u590D\u5B89\u88C5`]
3545
- };
3546
- }
3547
- }
3548
- if (opts.dryRun) {
3549
- return this.buildDryRunResult(plugin);
3550
- }
3551
- const rollback = {
3552
- createdVersionDir: null,
3553
- createdMarketplacePluginDir: null,
3554
- backupPath: null,
3555
- preExistingConfig: null
3556
- };
3557
- try {
3558
- return await this.doInstall(plugin, rollback);
3559
- } catch (err) {
3560
- await rollbackInstall(this.home, rollback);
3561
- if (isExpectedInstallError(err)) {
3562
- const message = err.message;
3563
- return {
3564
- target: "codex",
3565
- status: "failed",
3566
- included: [],
3567
- skipped: [],
3568
- artifacts: { files: [], mcpKeys: [] },
3569
- errors: [message],
3570
- notes: rollback.backupPath ? [`\u5DF2\u4ECE\u5907\u4EFD\u6062\u590D config.toml\uFF08${rollback.backupPath} \u5DF2\u5220\u9664\uFF09`] : void 0
3571
- };
3572
- }
3573
- throw err;
2646
+ await rollbackInstall(this.home, rollback);
2647
+ if (isExpectedInstallError(err)) {
2648
+ const message = err.message;
2649
+ return {
2650
+ target: "codex",
2651
+ status: "failed",
2652
+ included: [],
2653
+ skipped: [],
2654
+ artifacts: { files: [], mcpKeys: [] },
2655
+ errors: [message],
2656
+ notes: rollback.backupPath ? [`\u5DF2\u4ECE\u5907\u4EFD\u6062\u590D config.toml\uFF08${rollback.backupPath} \u5DF2\u5220\u9664\uFF09`] : void 0
2657
+ };
2658
+ }
2659
+ throw err;
3574
2660
  }
3575
2661
  }
3576
2662
  async uninstall(ref, opts = {}) {
@@ -3595,7 +2681,7 @@ var CodexInstaller = class {
3595
2681
  };
3596
2682
  }
3597
2683
  const cfgPath = codexConfigTomlPath(this.home);
3598
- if (!await pathExists4(cfgPath)) {
2684
+ if (!await pathExists2(cfgPath)) {
3599
2685
  return {
3600
2686
  target: "codex",
3601
2687
  status: "ok",
@@ -3687,11 +2773,11 @@ var CodexInstaller = class {
3687
2773
  ref.name
3688
2774
  );
3689
2775
  const cfgPath = codexConfigTomlPath(this.home);
3690
- const preExisted = await pathExists4(versionDir);
2776
+ const preExisted = await pathExists2(versionDir);
3691
2777
  if (preExisted) {
3692
- await rm3(versionDir, { recursive: true, force: true });
2778
+ await rm(versionDir, { recursive: true, force: true });
3693
2779
  }
3694
- await mkdir4(versionDir, { recursive: true });
2780
+ await mkdir2(versionDir, { recursive: true });
3695
2781
  rollback.createdVersionDir = versionDir;
3696
2782
  const dstSkills = codexPluginSkillsDir(versionDir);
3697
2783
  const writtenFiles = [versionDir];
@@ -3729,7 +2815,7 @@ var CodexInstaller = class {
3729
2815
  );
3730
2816
  if (mcpContent) {
3731
2817
  const mcpPath = codexPluginMcpPath(versionDir);
3732
- await writeFileAtomic2(mcpPath, serializeMcpJson(mcpContent));
2818
+ await writeFileAtomic(mcpPath, serializeMcpJson(mcpContent));
3733
2819
  writtenFiles.push(mcpPath);
3734
2820
  mcpJsonRef = "./.mcp.json";
3735
2821
  mcpKeys = listMcpKeys(mcpContent);
@@ -3739,7 +2825,7 @@ var CodexInstaller = class {
3739
2825
  }
3740
2826
  const pluginJsonObj = buildCodexPluginJson(plugin.manifest, mcpJsonRef);
3741
2827
  const pluginJsonPath = codexPluginManifestPath(versionDir);
3742
- await writeFileAtomic2(
2828
+ await writeFileAtomic(
3743
2829
  pluginJsonPath,
3744
2830
  `${JSON.stringify(pluginJsonObj, null, 2)}
3745
2831
  `
@@ -3857,12 +2943,12 @@ var CodexInstaller = class {
3857
2943
  const srcMcp = plugin.manifest.mcpServers;
3858
2944
  if (srcMcp !== void 0 && srcMcp !== null) {
3859
2945
  if (typeof srcMcp === "string") {
3860
- const destPath = resolve4(versionDir, srcMcp);
2946
+ const destPath = resolve3(versionDir, srcMcp);
3861
2947
  files.push(destPath);
3862
- const srcPath = resolve4(plugin.sourceDir, srcMcp);
3863
- const rel = pathRelative(resolve4(plugin.sourceDir), srcPath);
2948
+ const srcPath = resolve3(plugin.sourceDir, srcMcp);
2949
+ const rel = pathRelative(resolve3(plugin.sourceDir), srcPath);
3864
2950
  const traversal = rel === ".." || rel.startsWith("..") || rel.startsWith("/");
3865
- const exists = await pathExists4(srcPath);
2951
+ const exists = await pathExists2(srcPath);
3866
2952
  if (traversal || !exists) {
3867
2953
  dryRunStatus = "failed";
3868
2954
  dryRunErrors.push(
@@ -3870,7 +2956,7 @@ var CodexInstaller = class {
3870
2956
  );
3871
2957
  } else {
3872
2958
  try {
3873
- const raw = await readFile4(srcPath, "utf8");
2959
+ const raw = await readFile2(srcPath, "utf8");
3874
2960
  const parsed = JSON.parse(raw);
3875
2961
  if (parsed.mcpServers && typeof parsed.mcpServers === "object") {
3876
2962
  mcpKeys = Object.keys(parsed.mcpServers).sort();
@@ -3956,18 +3042,18 @@ async function rollbackInstall(home, rollback) {
3956
3042
  }
3957
3043
  );
3958
3044
  } else if (rollback.preExistingConfig === false) {
3959
- await rm3(cfgPath, { force: true }).catch(() => {
3045
+ await rm(cfgPath, { force: true }).catch(() => {
3960
3046
  });
3961
3047
  }
3962
3048
  if (rollback.createdVersionDir) {
3963
- await rm3(rollback.createdVersionDir, {
3049
+ await rm(rollback.createdVersionDir, {
3964
3050
  recursive: true,
3965
3051
  force: true
3966
3052
  }).catch(() => {
3967
3053
  });
3968
3054
  }
3969
3055
  if (rollback.createdMarketplacePluginDir) {
3970
- await rm3(rollback.createdMarketplacePluginDir, {
3056
+ await rm(rollback.createdMarketplacePluginDir, {
3971
3057
  recursive: true,
3972
3058
  force: true
3973
3059
  }).catch(() => {
@@ -4005,16 +3091,16 @@ function assertSafePathComponents(ref, version) {
4005
3091
  }
4006
3092
  async function copyAuthorProvidedMcp(sourceDir, versionDir, relativeRef) {
4007
3093
  if (typeof relativeRef !== "string" || relativeRef.length === 0) return null;
4008
- const srcPath = resolve4(sourceDir, relativeRef);
4009
- const rel = pathRelative(resolve4(sourceDir), srcPath);
3094
+ const srcPath = resolve3(sourceDir, relativeRef);
3095
+ const rel = pathRelative(resolve3(sourceDir), srcPath);
4010
3096
  if (rel === ".." || rel.startsWith("..") || rel.startsWith("/")) {
4011
3097
  return null;
4012
3098
  }
4013
- if (!await pathExists4(srcPath)) return null;
4014
- const destPath = resolve4(versionDir, relativeRef);
4015
- await mkdir4(dirname4(destPath), { recursive: true });
4016
- const raw = await readFile4(srcPath, "utf8");
4017
- await writeFileAtomic2(destPath, raw);
3099
+ if (!await pathExists2(srcPath)) return null;
3100
+ const destPath = resolve3(versionDir, relativeRef);
3101
+ await mkdir2(dirname2(destPath), { recursive: true });
3102
+ const raw = await readFile2(srcPath, "utf8");
3103
+ await writeFileAtomic(destPath, raw);
4018
3104
  let mcpKeys = [];
4019
3105
  let mcpServers = {};
4020
3106
  try {
@@ -4036,7 +3122,7 @@ async function planCodexSkills(plugin) {
4036
3122
  const usedNames = /* @__PURE__ */ new Set();
4037
3123
  const coveredAliases = /* @__PURE__ */ new Set();
4038
3124
  const commandFiles = await listMarkdownFiles(
4039
- resolve4(plugin.sourceDir, "commands"),
3125
+ resolve3(plugin.sourceDir, "commands"),
4040
3126
  {
4041
3127
  recursive: false
4042
3128
  }
@@ -4046,19 +3132,19 @@ async function planCodexSkills(plugin) {
4046
3132
  commandFiles.map((file2) => markdownSkillAlias(plugin.manifest.name, file2))
4047
3133
  )
4048
3134
  );
4049
- const nativeSkillsDir = resolve4(plugin.sourceDir, "skills");
3135
+ const nativeSkillsDir = resolve3(plugin.sourceDir, "skills");
4050
3136
  if (await hasCodexSkillDir(nativeSkillsDir)) {
4051
3137
  for (const name of await listCodexSkillNames(nativeSkillsDir)) {
4052
3138
  usedNames.add(name);
4053
3139
  coveredAliases.add(skillAlias(plugin.manifest.name, name));
4054
3140
  sources.push({
4055
3141
  kind: "copy-skill",
4056
- sourceDir: resolve4(nativeSkillsDir, name),
3142
+ sourceDir: resolve3(nativeSkillsDir, name),
4057
3143
  skillName: name
4058
3144
  });
4059
3145
  }
4060
3146
  } else {
4061
- const dotSkillsDir = resolve4(plugin.sourceDir, ".skills");
3147
+ const dotSkillsDir = resolve3(plugin.sourceDir, ".skills");
4062
3148
  const claudeSkills = await listMarkdownFiles(dotSkillsDir);
4063
3149
  const claudeAliases = /* @__PURE__ */ new Map();
4064
3150
  for (const file2 of claudeSkills) {
@@ -4097,7 +3183,7 @@ async function planCodexSkills(plugin) {
4097
3183
  async function materializeCodexSkills(plan, dstSkills) {
4098
3184
  for (const source of plan.sources) {
4099
3185
  if (source.kind === "copy-skill") {
4100
- await cp2(source.sourceDir, resolve4(dstSkills, source.skillName), {
3186
+ await cp(source.sourceDir, resolve3(dstSkills, source.skillName), {
4101
3187
  recursive: true,
4102
3188
  dereference: false,
4103
3189
  preserveTimestamps: true,
@@ -4105,10 +3191,10 @@ async function materializeCodexSkills(plan, dstSkills) {
4105
3191
  });
4106
3192
  continue;
4107
3193
  }
4108
- const raw = await readFile4(source.sourceFile, "utf8");
3194
+ const raw = await readFile2(source.sourceFile, "utf8");
4109
3195
  const normalized = normalizeSkillMarkdown(raw, source.skillName);
4110
- await writeFileAtomic2(
4111
- resolve4(dstSkills, source.skillName, "SKILL.md"),
3196
+ await writeFileAtomic(
3197
+ resolve3(dstSkills, source.skillName, "SKILL.md"),
4112
3198
  normalized
4113
3199
  );
4114
3200
  }
@@ -4184,35 +3270,35 @@ async function hasCodexSkillDir(skillsDir) {
4184
3270
  return (await listCodexSkillNames(skillsDir)).length > 0;
4185
3271
  }
4186
3272
  async function listCodexSkillNames(skillsDir) {
4187
- if (!await isDir2(skillsDir)) return [];
3273
+ if (!await isDir(skillsDir)) return [];
4188
3274
  let entries;
4189
3275
  try {
4190
- entries = await readdir2(skillsDir, { withFileTypes: true });
3276
+ entries = await readdir(skillsDir, { withFileTypes: true });
4191
3277
  } catch {
4192
3278
  return [];
4193
3279
  }
4194
3280
  const names = [];
4195
3281
  for (const entry of entries) {
4196
3282
  if (!entry.isDirectory()) continue;
4197
- if (await pathExists4(resolve4(skillsDir, entry.name, "SKILL.md"))) {
3283
+ if (await pathExists2(resolve3(skillsDir, entry.name, "SKILL.md"))) {
4198
3284
  names.push(entry.name);
4199
3285
  }
4200
3286
  }
4201
3287
  return names.sort();
4202
3288
  }
4203
3289
  async function listMarkdownFiles(dir, opts = {}) {
4204
- if (!await isDir2(dir)) return [];
3290
+ if (!await isDir(dir)) return [];
4205
3291
  const recursive = opts.recursive !== false;
4206
3292
  const out = [];
4207
3293
  async function walk2(current) {
4208
3294
  let entries;
4209
3295
  try {
4210
- entries = await readdir2(current, { withFileTypes: true });
3296
+ entries = await readdir(current, { withFileTypes: true });
4211
3297
  } catch {
4212
3298
  return;
4213
3299
  }
4214
3300
  for (const entry of entries) {
4215
- const abs = resolve4(current, entry.name);
3301
+ const abs = resolve3(current, entry.name);
4216
3302
  if (entry.isDirectory()) {
4217
3303
  if (recursive) await walk2(abs);
4218
3304
  continue;
@@ -4237,10 +3323,10 @@ function uniqueSkillName(used, desired) {
4237
3323
  return candidate;
4238
3324
  }
4239
3325
  async function markdownSkillAlias(pluginName, filePath) {
4240
- const triggerAlias = extractTriggerAlias(await readFile4(filePath, "utf8"));
3326
+ const triggerAlias = extractTriggerAlias(await readFile2(filePath, "utf8"));
4241
3327
  return skillAlias(
4242
3328
  pluginName,
4243
- triggerAlias ?? basename3(filePath, extname(filePath))
3329
+ triggerAlias ?? basename2(filePath, extname(filePath))
4244
3330
  );
4245
3331
  }
4246
3332
  function markdownPathAlias(pluginName, baseDir, filePath) {
@@ -4266,30 +3352,30 @@ function slugify(value) {
4266
3352
  const slug = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4267
3353
  return slug.length > 0 ? slug : "skill";
4268
3354
  }
4269
- async function writeFileAtomic2(filePath, content) {
4270
- await mkdir4(dirname4(filePath), { recursive: true });
3355
+ async function writeFileAtomic(filePath, content) {
3356
+ await mkdir2(dirname2(filePath), { recursive: true });
4271
3357
  const tmp = `${filePath}.${Math.random().toString(36).slice(2)}.tmp`;
4272
3358
  try {
4273
- await writeFile4(tmp, content, { encoding: "utf8", flag: "w" });
4274
- const { rename: rename10 } = await import("fs/promises");
4275
- await rename10(tmp, filePath);
3359
+ await writeFile2(tmp, content, { encoding: "utf8", flag: "w" });
3360
+ const { rename: rename7 } = await import("fs/promises");
3361
+ await rename7(tmp, filePath);
4276
3362
  } catch (err) {
4277
- await rm3(tmp, { force: true }).catch(() => {
3363
+ await rm(tmp, { force: true }).catch(() => {
4278
3364
  });
4279
3365
  throw err;
4280
3366
  }
4281
3367
  }
4282
- async function pathExists4(p) {
3368
+ async function pathExists2(p) {
4283
3369
  try {
4284
- await access3(p, fsConstants3.F_OK);
3370
+ await access(p, fsConstants.F_OK);
4285
3371
  return true;
4286
3372
  } catch {
4287
3373
  return false;
4288
3374
  }
4289
3375
  }
4290
- async function isDir2(p) {
3376
+ async function isDir(p) {
4291
3377
  try {
4292
- const s = await stat3(p);
3378
+ const s = await stat(p);
4293
3379
  return s.isDirectory();
4294
3380
  } catch {
4295
3381
  return false;
@@ -4304,28 +3390,28 @@ function parsePluginKey(key) {
4304
3390
  return { name: key.slice(0, at), marketplace: key.slice(at + 1) };
4305
3391
  }
4306
3392
  async function isFullyInstalledMirror(home, ref) {
4307
- const mktDir = resolve4(
3393
+ const mktDir = resolve3(
4308
3394
  codexHomeDir(home),
4309
3395
  "plugins",
4310
3396
  "marketplaces",
4311
3397
  ref.marketplace
4312
3398
  );
4313
- const pluginDir = resolve4(mktDir, "plugins", ref.name);
4314
- if (!await isDir2(pluginDir)) return false;
4315
- const manifestPath = resolve4(pluginDir, ".codex-plugin", "plugin.json");
4316
- if (await pathExists4(manifestPath)) {
3399
+ const pluginDir = resolve3(mktDir, "plugins", ref.name);
3400
+ if (!await isDir(pluginDir)) return false;
3401
+ const manifestPath = resolve3(pluginDir, ".codex-plugin", "plugin.json");
3402
+ if (await pathExists2(manifestPath)) {
4317
3403
  try {
4318
- const raw = await readFile4(manifestPath, "utf8");
3404
+ const raw = await readFile2(manifestPath, "utf8");
4319
3405
  const parsed = JSON.parse(raw);
4320
3406
  if (parsed["x-rush-mirror"] === "stub") return false;
4321
3407
  } catch {
4322
3408
  return false;
4323
3409
  }
4324
3410
  }
4325
- const mcpPath = resolve4(pluginDir, ".mcp.json");
4326
- if (await pathExists4(mcpPath)) {
3411
+ const mcpPath = resolve3(pluginDir, ".mcp.json");
3412
+ if (await pathExists2(mcpPath)) {
4327
3413
  try {
4328
- const raw = await readFile4(mcpPath, "utf8");
3414
+ const raw = await readFile2(mcpPath, "utf8");
4329
3415
  if (/\$\{[^}]+\}/.test(raw)) return false;
4330
3416
  } catch {
4331
3417
  return false;
@@ -4334,30 +3420,30 @@ async function isFullyInstalledMirror(home, ref) {
4334
3420
  return true;
4335
3421
  }
4336
3422
  async function detectInstalledVersion(home, ref) {
4337
- const baseDir = resolve4(
3423
+ const baseDir = resolve3(
4338
3424
  codexHomeDir(home),
4339
3425
  "plugins",
4340
3426
  "cache",
4341
3427
  ref.marketplace,
4342
3428
  ref.name
4343
3429
  );
4344
- if (!await isDir2(baseDir)) return null;
3430
+ if (!await isDir(baseDir)) return null;
4345
3431
  let entries;
4346
3432
  try {
4347
- entries = await readdir2(baseDir);
3433
+ entries = await readdir(baseDir);
4348
3434
  } catch {
4349
3435
  return null;
4350
3436
  }
4351
3437
  let best = null;
4352
3438
  for (const entry of entries) {
4353
- const versionDir = resolve4(baseDir, entry);
4354
- const manifestPath = resolve4(versionDir, ".codex-plugin", "plugin.json");
4355
- if (!await pathExists4(manifestPath)) continue;
3439
+ const versionDir = resolve3(baseDir, entry);
3440
+ const manifestPath = resolve3(versionDir, ".codex-plugin", "plugin.json");
3441
+ if (!await pathExists2(manifestPath)) continue;
4356
3442
  try {
4357
- const raw = await readFile4(manifestPath, "utf8");
3443
+ const raw = await readFile2(manifestPath, "utf8");
4358
3444
  const obj = JSON.parse(raw);
4359
3445
  const version = typeof obj.version === "string" && obj.version.length > 0 ? obj.version : entry;
4360
- const s = await stat3(versionDir);
3446
+ const s = await stat(versionDir);
4361
3447
  if (!best || s.mtimeMs > best.mtimeMs) {
4362
3448
  best = { version, mtimeMs: s.mtimeMs };
4363
3449
  }
@@ -4379,28 +3465,28 @@ function defaultRushAiBinaryResolver() {
4379
3465
  }
4380
3466
 
4381
3467
  // src/marketplaces/cache.ts
4382
- import { randomUUID as randomUUID4 } from "crypto";
4383
- import { constants as fsConstants6 } from "fs";
3468
+ import { randomUUID } from "crypto";
3469
+ import { constants as fsConstants4 } from "fs";
4384
3470
  import {
4385
- access as access6,
4386
- mkdir as mkdir6,
4387
- readdir as readdir3,
4388
- readFile as readFile6,
4389
- rename as rename4,
4390
- rm as rm6,
4391
- writeFile as writeFile6
3471
+ access as access4,
3472
+ mkdir as mkdir3,
3473
+ readdir as readdir2,
3474
+ readFile as readFile4,
3475
+ rename,
3476
+ rm as rm3,
3477
+ writeFile as writeFile3
4392
3478
  } from "fs/promises";
4393
3479
  import { homedir as homedir4 } from "os";
4394
- import { dirname as dirname5, resolve as resolve9 } from "path";
3480
+ import { dirname as dirname3, resolve as resolve7 } from "path";
4395
3481
 
4396
3482
  // src/marketplaces/directory.ts
4397
3483
  import { stat as fsStat } from "fs/promises";
4398
- import { resolve as resolve6 } from "path";
3484
+ import { resolve as resolve5 } from "path";
4399
3485
 
4400
3486
  // src/marketplaces/manifest.ts
4401
- import { constants as fsConstants4 } from "fs";
4402
- import { access as access4, readFile as readFile5 } from "fs/promises";
4403
- import { resolve as resolve5 } from "path";
3487
+ import { constants as fsConstants2 } from "fs";
3488
+ import { access as access2, readFile as readFile3 } from "fs/promises";
3489
+ import { resolve as resolve4 } from "path";
4404
3490
 
4405
3491
  // src/marketplaces/source.ts
4406
3492
  import { isAbsolute as isAbsolute2 } from "path";
@@ -4599,9 +3685,9 @@ var MarketplaceCorruptError = class extends MarketplaceError {
4599
3685
  }
4600
3686
  };
4601
3687
  async function readMarketplaceJson(rootDir) {
4602
- const filePath = resolve5(rootDir, MARKETPLACE_MANIFEST_RELATIVE_PATH);
3688
+ const filePath = resolve4(rootDir, MARKETPLACE_MANIFEST_RELATIVE_PATH);
4603
3689
  try {
4604
- await access4(filePath, fsConstants4.R_OK);
3690
+ await access2(filePath, fsConstants2.R_OK);
4605
3691
  } catch {
4606
3692
  throw new MarketplaceCorruptError(
4607
3693
  filePath,
@@ -4609,7 +3695,7 @@ async function readMarketplaceJson(rootDir) {
4609
3695
  "file does not exist or is not readable"
4610
3696
  );
4611
3697
  }
4612
- const raw = await readFile5(filePath, "utf8");
3698
+ const raw = await readFile3(filePath, "utf8");
4613
3699
  return parseMarketplaceJson(raw, filePath);
4614
3700
  }
4615
3701
  function parseMarketplaceJson(raw, sourcePathForError) {
@@ -4675,7 +3761,7 @@ async function resolveDirectoryMarketplace(source, name) {
4675
3761
  "marketplace name must be non-empty string"
4676
3762
  );
4677
3763
  }
4678
- const rootDir = resolve6(source.path);
3764
+ const rootDir = resolve5(source.path);
4679
3765
  let stats;
4680
3766
  try {
4681
3767
  stats = await fsStat(rootDir);
@@ -4714,9 +3800,9 @@ async function ensureDirectoryExists(source) {
4714
3800
 
4715
3801
  // src/marketplaces/github.ts
4716
3802
  import { spawn } from "child_process";
4717
- import { constants as fsConstants5 } from "fs";
4718
- import { access as access5, rm as rm4 } from "fs/promises";
4719
- import { resolve as resolve7 } from "path";
3803
+ import { constants as fsConstants3 } from "fs";
3804
+ import { access as access3, rm as rm2 } from "fs/promises";
3805
+ import { resolve as resolve6 } from "path";
4720
3806
  var GitCloneFailedError = class extends MarketplaceError {
4721
3807
  constructor(source, exitCode, stderr) {
4722
3808
  super(
@@ -4769,7 +3855,7 @@ async function cloneGithubMarketplace(source, cacheDir, name, opts) {
4769
3855
  if (existing === "missing") {
4770
3856
  await runClone(runner, url, cacheDir, source);
4771
3857
  } else if (existing === "not-git") {
4772
- await rm4(cacheDir, { recursive: true, force: true });
3858
+ await rm2(cacheDir, { recursive: true, force: true });
4773
3859
  await runClone(runner, url, cacheDir, source);
4774
3860
  } else {
4775
3861
  await runUpdate(runner, cacheDir, source);
@@ -4777,7 +3863,7 @@ async function cloneGithubMarketplace(source, cacheDir, name, opts) {
4777
3863
  const manifest = await readMarketplaceJson(cacheDir);
4778
3864
  return {
4779
3865
  name,
4780
- rootDir: resolve7(cacheDir),
3866
+ rootDir: resolve6(cacheDir),
4781
3867
  source,
4782
3868
  manifest
4783
3869
  };
@@ -4785,264 +3871,66 @@ async function cloneGithubMarketplace(source, cacheDir, name, opts) {
4785
3871
  async function updateGithubMarketplace(source, cacheDir, name, opts) {
4786
3872
  const runner = opts?.runner ?? defaultGitRunner;
4787
3873
  const state = await detectCacheState(cacheDir);
4788
- if (state !== "git") {
4789
- return cloneGithubMarketplace(source, cacheDir, name, opts);
4790
- }
4791
- await runUpdate(runner, cacheDir, source);
4792
- const manifest = await readMarketplaceJson(cacheDir);
4793
- return {
4794
- name,
4795
- rootDir: resolve7(cacheDir),
4796
- source,
4797
- manifest
4798
- };
4799
- }
4800
- async function runClone(runner, url, cacheDir, source) {
4801
- const args = ["clone", "--depth", "1"];
4802
- if (source.ref) {
4803
- args.push("--branch", source.ref);
4804
- }
4805
- args.push("--", url, cacheDir);
4806
- const result = await runner(args);
4807
- if (result.exitCode !== 0) {
4808
- throw new GitCloneFailedError(source, result.exitCode, result.stderr);
4809
- }
4810
- }
4811
- async function runUpdate(runner, cacheDir, source) {
4812
- const refArg = source.ref ?? "HEAD";
4813
- const fetchResult = await runner(
4814
- ["fetch", "--depth", "1", "origin", refArg],
4815
- { cwd: cacheDir }
4816
- );
4817
- if (fetchResult.exitCode !== 0) {
4818
- throw new GitCloneFailedError(
4819
- source,
4820
- fetchResult.exitCode,
4821
- fetchResult.stderr
4822
- );
4823
- }
4824
- const checkoutResult = await runner(
4825
- ["checkout", "--quiet", "--detach", "FETCH_HEAD"],
4826
- { cwd: cacheDir }
4827
- );
4828
- if (checkoutResult.exitCode !== 0) {
4829
- throw new GitCloneFailedError(
4830
- source,
4831
- checkoutResult.exitCode,
4832
- checkoutResult.stderr
4833
- );
4834
- }
4835
- }
4836
- async function detectCacheState(cacheDir) {
4837
- try {
4838
- await access5(cacheDir, fsConstants5.F_OK);
4839
- } catch {
4840
- return "missing";
4841
- }
4842
- try {
4843
- await access5(resolve7(cacheDir, ".git"), fsConstants5.F_OK);
4844
- return "git";
4845
- } catch {
4846
- return "not-git";
4847
- }
4848
- }
4849
-
4850
- // src/marketplaces/rush.ts
4851
- import { execFileSync as execFileSync4 } from "child_process";
4852
- import { randomUUID as randomUUID3 } from "crypto";
4853
- import { mkdir as mkdir5, rename as rename3, rm as rm5, stat as stat4, writeFile as writeFile5 } from "fs/promises";
4854
- import { resolve as resolve8 } from "path";
4855
- async function fetchRushMarketplace(source, opts = {}) {
4856
- const fetchFn = opts.fetchFn ?? fetch;
4857
- const url = `${inferProtocol(source.host)}${source.host}/api/marketplace`;
4858
- const headers = {
4859
- Accept: "application/json"
4860
- };
4861
- if (opts.token) {
4862
- headers.Authorization = `Bearer ${opts.token}`;
4863
- }
4864
- const response = await fetchFn(url, { headers });
4865
- if (!response.ok) {
4866
- throw new Error(
4867
- `Failed to fetch rush marketplace from ${url}: ${response.status} ${response.statusText}`
4868
- );
4869
- }
4870
- const data = await response.json();
4871
- if (!data.plugins || !Array.isArray(data.plugins)) {
4872
- throw new Error(
4873
- `Invalid marketplace response from ${url}: missing 'plugins' array`
4874
- );
4875
- }
4876
- return data;
4877
- }
4878
- async function fetchRushPlugin(source, pluginSlug, opts = {}) {
4879
- const fetchFn = opts.fetchFn ?? fetch;
4880
- const url = `${inferProtocol(source.host)}${source.host}/api/marketplace/${encodeURIComponent(pluginSlug)}`;
4881
- const headers = {
4882
- Accept: "application/json"
4883
- };
4884
- if (opts.token) {
4885
- headers.Authorization = `Bearer ${opts.token}`;
4886
- }
4887
- const response = await fetchFn(url, { headers });
4888
- if (response.status === 401) {
4889
- throw new Error(
4890
- `Plugin '${pluginSlug}' requires authentication. Run 'rush-ai login' first.`
4891
- );
4892
- }
4893
- if (response.status === 404) {
4894
- throw new Error(
4895
- `Plugin '${pluginSlug}' not found in rush marketplace at ${source.host}`
4896
- );
4897
- }
4898
- if (!response.ok) {
4899
- throw new Error(
4900
- `Failed to fetch plugin '${pluginSlug}' from ${url}: ${response.status} ${response.statusText}`
4901
- );
4902
- }
4903
- return await response.json();
4904
- }
4905
- async function materializeRushPlugin(source, pluginSlug, targetDir, opts = {}) {
4906
- validateSlug(pluginSlug, "plugin slug");
4907
- const manifest = await fetchRushPlugin(source, pluginSlug, opts);
4908
- let mcpServers = {};
4909
- if (manifest.mcpServers && Object.keys(manifest.mcpServers).length > 0) {
4910
- mcpServers = { ...manifest.mcpServers };
4911
- if (opts.secrets && Object.keys(opts.secrets).length > 0) {
4912
- mcpServers = substituteMcpSecrets(mcpServers, opts.secrets);
4913
- }
4914
- }
4915
- const pluginJson = {
4916
- name: manifest.name,
4917
- version: manifest.version || "1.0.0",
4918
- description: manifest.description,
4919
- ...Object.keys(mcpServers).length > 0 ? { mcpServers } : {}
4920
- };
4921
- const tmpDir = `${targetDir}.${randomUUID3()}.tmp`;
4922
- try {
4923
- const pluginJsonDir = resolve8(tmpDir, ".claude-plugin");
4924
- await mkdir5(pluginJsonDir, { recursive: true });
4925
- await writeFile5(
4926
- resolve8(pluginJsonDir, "plugin.json"),
4927
- `${JSON.stringify(pluginJson, null, 2)}
4928
- `,
4929
- "utf8"
4930
- );
4931
- if (manifest.skills && manifest.skills.length > 0) {
4932
- await writeFile5(
4933
- resolve8(tmpDir, "skills.json"),
4934
- '{"skills":{}}\n',
4935
- "utf8"
4936
- );
4937
- const registryUrl = `${inferProtocol(source.host)}${source.host}`;
4938
- for (const skill of manifest.skills) {
4939
- try {
4940
- const args = [
4941
- "-y",
4942
- "reskill@latest",
4943
- "install",
4944
- skill.name,
4945
- "--no-save",
4946
- "--skip-manifest",
4947
- "-y",
4948
- "-f",
4949
- "-r",
4950
- registryUrl,
4951
- ...opts.token ? ["-t", opts.token] : []
4952
- ];
4953
- execFileSync4("npx", args, {
4954
- cwd: tmpDir,
4955
- timeout: 6e4,
4956
- stdio: "pipe"
4957
- });
4958
- } catch (err) {
4959
- const stderr = err && typeof err === "object" && "stderr" in err ? err.stderr?.toString() ?? "" : "";
4960
- if (stderr.includes("401") || stderr.includes("403") || stderr.includes("Unauthorized") || stderr.includes("Forbidden")) {
4961
- throw new SkillAuthError(skill.name, 401);
4962
- }
4963
- output.warn(
4964
- ` Warning: failed to install skill '${skill.name}': ${err instanceof Error ? err.message : String(err)}`
4965
- );
4966
- }
4967
- }
4968
- }
4969
- const dotSkillsDir = resolve8(tmpDir, ".skills");
4970
- const skillsDir = resolve8(tmpDir, "skills");
4971
- if (await stat4(dotSkillsDir).then((s) => s.isDirectory()).catch(() => false)) {
4972
- if (!await stat4(skillsDir).then((s) => s.isDirectory()).catch(() => false)) {
4973
- await rename3(dotSkillsDir, skillsDir);
4974
- }
4975
- }
4976
- await rm5(resolve8(tmpDir, "skills.json"), { force: true });
4977
- await rm5(resolve8(tmpDir, ".cursor"), { recursive: true, force: true });
4978
- await rm5(resolve8(tmpDir, ".claude"), { recursive: true, force: true });
4979
- await rm5(resolve8(tmpDir, ".codex"), { recursive: true, force: true });
4980
- await rm5(resolve8(tmpDir, ".github"), { recursive: true, force: true });
4981
- await rm5(resolve8(tmpDir, ".opencode"), { recursive: true, force: true });
4982
- await rm5(targetDir, { recursive: true, force: true });
4983
- await mkdir5(resolve8(targetDir, ".."), { recursive: true });
4984
- await rename3(tmpDir, targetDir);
4985
- } catch (err) {
4986
- await rm5(tmpDir, { recursive: true, force: true }).catch(() => {
4987
- });
4988
- throw err;
3874
+ if (state !== "git") {
3875
+ return cloneGithubMarketplace(source, cacheDir, name, opts);
4989
3876
  }
4990
- return manifest;
3877
+ await runUpdate(runner, cacheDir, source);
3878
+ const manifest = await readMarketplaceJson(cacheDir);
3879
+ return {
3880
+ name,
3881
+ rootDir: resolve6(cacheDir),
3882
+ source,
3883
+ manifest
3884
+ };
4991
3885
  }
4992
- var SkillAuthError = class extends Error {
4993
- constructor(skillName, status) {
4994
- super(
4995
- `Skill '${skillName}' requires authentication (HTTP ${status}). Run 'rush-ai auth login' first.`
4996
- );
4997
- this.name = "SkillAuthError";
3886
+ async function runClone(runner, url, cacheDir, source) {
3887
+ const args = ["clone", "--depth", "1"];
3888
+ if (source.ref) {
3889
+ args.push("--branch", source.ref);
4998
3890
  }
4999
- };
5000
- function substituteMcpSecrets(mcpServers, secrets) {
5001
- const result = {};
5002
- for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
5003
- if (!serverConfig || typeof serverConfig !== "object") {
5004
- result[serverName] = serverConfig;
5005
- continue;
5006
- }
5007
- const cfg = { ...serverConfig };
5008
- if (cfg.env && typeof cfg.env === "object") {
5009
- const env = {};
5010
- for (const [k, v] of Object.entries(cfg.env)) {
5011
- env[k] = substituteValue(v, secrets);
5012
- }
5013
- cfg.env = env;
5014
- }
5015
- if (cfg.headers && typeof cfg.headers === "object") {
5016
- const headers = {};
5017
- for (const [k, v] of Object.entries(
5018
- cfg.headers
5019
- )) {
5020
- headers[k] = substituteValue(v, secrets);
5021
- }
5022
- cfg.headers = headers;
5023
- }
5024
- result[serverName] = cfg;
3891
+ args.push("--", url, cacheDir);
3892
+ const result = await runner(args);
3893
+ if (result.exitCode !== 0) {
3894
+ throw new GitCloneFailedError(source, result.exitCode, result.stderr);
5025
3895
  }
5026
- return result;
5027
- }
5028
- function substituteValue(value, secrets) {
5029
- return value.replace(/\$\{([^}]+)\}/g, (match, key) => {
5030
- return key in secrets ? secrets[key] : match;
5031
- });
5032
3896
  }
5033
- function validateSlug(value, label) {
5034
- if (!value || value.includes("..") || value.startsWith("/") || value.includes("\0")) {
5035
- throw new Error(
5036
- `Invalid ${label} '${value}': must not be empty, contain '..', start with '/', or contain null bytes.`
3897
+ async function runUpdate(runner, cacheDir, source) {
3898
+ const refArg = source.ref ?? "HEAD";
3899
+ const fetchResult = await runner(
3900
+ ["fetch", "--depth", "1", "origin", refArg],
3901
+ { cwd: cacheDir }
3902
+ );
3903
+ if (fetchResult.exitCode !== 0) {
3904
+ throw new GitCloneFailedError(
3905
+ source,
3906
+ fetchResult.exitCode,
3907
+ fetchResult.stderr
3908
+ );
3909
+ }
3910
+ const checkoutResult = await runner(
3911
+ ["checkout", "--quiet", "--detach", "FETCH_HEAD"],
3912
+ { cwd: cacheDir }
3913
+ );
3914
+ if (checkoutResult.exitCode !== 0) {
3915
+ throw new GitCloneFailedError(
3916
+ source,
3917
+ checkoutResult.exitCode,
3918
+ checkoutResult.stderr
5037
3919
  );
5038
3920
  }
5039
3921
  }
5040
- function inferProtocol(host) {
5041
- const hostname = host.split(":")[0];
5042
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0") {
5043
- return "http://";
3922
+ async function detectCacheState(cacheDir) {
3923
+ try {
3924
+ await access3(cacheDir, fsConstants3.F_OK);
3925
+ } catch {
3926
+ return "missing";
3927
+ }
3928
+ try {
3929
+ await access3(resolve6(cacheDir, ".git"), fsConstants3.F_OK);
3930
+ return "git";
3931
+ } catch {
3932
+ return "not-git";
5044
3933
  }
5045
- return "https://";
5046
3934
  }
5047
3935
 
5048
3936
  // src/marketplaces/cache.ts
@@ -5071,10 +3959,10 @@ var MarketplaceCache = class {
5071
3959
  gitRunner;
5072
3960
  constructor(opts = {}) {
5073
3961
  if (opts.cacheBaseDir) {
5074
- this.cacheBaseDir = resolve9(opts.cacheBaseDir);
3962
+ this.cacheBaseDir = resolve7(opts.cacheBaseDir);
5075
3963
  } else {
5076
3964
  const home = opts.home ?? homedir4();
5077
- this.cacheBaseDir = resolve9(home, MARKETPLACE_CACHE_RELATIVE_PATH);
3965
+ this.cacheBaseDir = resolve7(home, MARKETPLACE_CACHE_RELATIVE_PATH);
5078
3966
  }
5079
3967
  if (opts.gitRunner) {
5080
3968
  this.gitRunner = opts.gitRunner;
@@ -5087,7 +3975,7 @@ var MarketplaceCache = class {
5087
3975
  `Invalid marketplace name '${name}'. Names must be non-empty and cannot contain '/' or '..'.`
5088
3976
  );
5089
3977
  }
5090
- return resolve9(this.cacheBaseDir, name);
3978
+ return resolve7(this.cacheBaseDir, name);
5091
3979
  }
5092
3980
  /**
5093
3981
  * 添加一个 marketplace 到本地 cache。
@@ -5127,7 +4015,7 @@ var MarketplaceCache = class {
5127
4015
  return resolved;
5128
4016
  }
5129
4017
  case "github": {
5130
- await mkdir6(dirname5(targetDir), { recursive: true });
4018
+ await mkdir3(dirname3(targetDir), { recursive: true });
5131
4019
  const resolved = await cloneGithubMarketplace(
5132
4020
  source,
5133
4021
  targetDir,
@@ -5156,10 +4044,10 @@ var MarketplaceCache = class {
5156
4044
  */
5157
4045
  async remove(name) {
5158
4046
  const target = this.pathFor(name);
5159
- if (!await pathExists5(target)) {
4047
+ if (!await pathExists3(target)) {
5160
4048
  return;
5161
4049
  }
5162
- await rm6(target, { recursive: true, force: true });
4050
+ await rm3(target, { recursive: true, force: true });
5163
4051
  }
5164
4052
  /**
5165
4053
  * 重新 fetch 远端 source(仅对 github: / git: / rush:// / npm:)。
@@ -5207,10 +4095,10 @@ var MarketplaceCache = class {
5207
4095
  * v1 暂不单列损坏项;若未来需要,可加 `options.includeCorrupt` 返回 warning。
5208
4096
  */
5209
4097
  async list() {
5210
- if (!await pathExists5(this.cacheBaseDir)) {
4098
+ if (!await pathExists3(this.cacheBaseDir)) {
5211
4099
  return [];
5212
4100
  }
5213
- const entries = await readdir3(this.cacheBaseDir, { withFileTypes: true });
4101
+ const entries = await readdir2(this.cacheBaseDir, { withFileTypes: true });
5214
4102
  const result = [];
5215
4103
  for (const entry of entries) {
5216
4104
  if (!entry.isDirectory()) continue;
@@ -5218,7 +4106,7 @@ var MarketplaceCache = class {
5218
4106
  if (!meta) continue;
5219
4107
  result.push({
5220
4108
  name: entry.name,
5221
- path: resolve9(this.cacheBaseDir, entry.name),
4109
+ path: resolve7(this.cacheBaseDir, entry.name),
5222
4110
  source: meta.source,
5223
4111
  cachedAt: meta.cachedAt
5224
4112
  });
@@ -5291,20 +4179,20 @@ var MarketplaceCache = class {
5291
4179
  };
5292
4180
  const resolvedName = name ?? defaultNameFromSource(source);
5293
4181
  const rootDir = this.pathFor(resolvedName);
5294
- const manifestDir = resolve9(rootDir, ".claude-plugin");
5295
- await mkdir6(manifestDir, { recursive: true });
5296
- await atomicWriteJson(resolve9(manifestDir, "marketplace.json"), manifest);
4182
+ const manifestDir = resolve7(rootDir, ".claude-plugin");
4183
+ await mkdir3(manifestDir, { recursive: true });
4184
+ await atomicWriteJson(resolve7(manifestDir, "marketplace.json"), manifest);
5297
4185
  return { name: resolvedName, rootDir, source, manifest };
5298
4186
  }
5299
4187
  metaPathFor(name) {
5300
- return resolve9(this.pathFor(name), CACHE_META_FILENAME);
4188
+ return resolve7(this.pathFor(name), CACHE_META_FILENAME);
5301
4189
  }
5302
4190
  async readMeta(name) {
5303
4191
  const metaPath = this.metaPathFor(name);
5304
- if (!await pathExists5(metaPath)) {
4192
+ if (!await pathExists3(metaPath)) {
5305
4193
  return null;
5306
4194
  }
5307
- const raw = await readFile6(metaPath, "utf8");
4195
+ const raw = await readFile4(metaPath, "utf8");
5308
4196
  let parsed;
5309
4197
  try {
5310
4198
  parsed = JSON.parse(raw);
@@ -5334,7 +4222,7 @@ var MarketplaceCache = class {
5334
4222
  }
5335
4223
  async writeMeta(name, source) {
5336
4224
  const metaPath = this.metaPathFor(name);
5337
- await mkdir6(dirname5(metaPath), { recursive: true });
4225
+ await mkdir3(dirname3(metaPath), { recursive: true });
5338
4226
  const payload = {
5339
4227
  sourceRaw: source.raw,
5340
4228
  cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -5344,9 +4232,9 @@ var MarketplaceCache = class {
5344
4232
  await atomicWriteJson(metaPath, payload);
5345
4233
  }
5346
4234
  };
5347
- async function pathExists5(p) {
4235
+ async function pathExists3(p) {
5348
4236
  try {
5349
- await access6(p, fsConstants6.F_OK);
4237
+ await access4(p, fsConstants4.F_OK);
5350
4238
  return true;
5351
4239
  } catch {
5352
4240
  return false;
@@ -5355,237 +4243,17 @@ async function pathExists5(p) {
5355
4243
  async function atomicWriteJson(filePath, payload) {
5356
4244
  const content = `${JSON.stringify(payload, null, 2)}
5357
4245
  `;
5358
- const tmp = `${filePath}.${randomUUID4()}.tmp`;
4246
+ const tmp = `${filePath}.${randomUUID()}.tmp`;
5359
4247
  try {
5360
- await writeFile6(tmp, content, { encoding: "utf8" });
5361
- await rename4(tmp, filePath);
4248
+ await writeFile3(tmp, content, { encoding: "utf8" });
4249
+ await rename(tmp, filePath);
5362
4250
  } catch (err) {
5363
- await rm6(tmp, { force: true }).catch(() => {
4251
+ await rm3(tmp, { force: true }).catch(() => {
5364
4252
  });
5365
4253
  throw err;
5366
4254
  }
5367
4255
  }
5368
4256
 
5369
- // src/commands/marketplace/_codex-content-loader.ts
5370
- import { constants as fsConstants7 } from "fs";
5371
- import { access as access7, readFile as readFile7, stat as stat5 } from "fs/promises";
5372
- import { resolve as pathResolve2, sep } from "path";
5373
- function pickContentLoader(resolved, opts = {}) {
5374
- switch (resolved.source.kind) {
5375
- case "rush":
5376
- return buildRushPluginContentLoader(resolved.source, opts);
5377
- case "directory":
5378
- case "github":
5379
- return buildLocalCachePluginContentLoader(resolved);
5380
- default:
5381
- return async () => ({});
5382
- }
5383
- }
5384
- function buildRushPluginContentLoader(source, opts = {}) {
5385
- const token = opts.token ?? getAuthToken();
5386
- const fetchOpts = {};
5387
- if (opts.fetchFn !== void 0) fetchOpts.fetchFn = opts.fetchFn;
5388
- if (token !== void 0 && token !== null) fetchOpts.token = token;
5389
- const webBase = inferWebBase(source.host);
5390
- const fetchFn = opts.fetchFn ?? globalThis.fetch;
5391
- return async (entry) => {
5392
- if (typeof entry.name !== "string" || entry.name.length === 0) return {};
5393
- try {
5394
- const detail = await fetchRushPlugin(source, entry.name, fetchOpts);
5395
- const manifest = pickManifestFields(
5396
- detail
5397
- );
5398
- const hasMcp2 = detail.mcpServers && Object.keys(detail.mcpServers).length > 0;
5399
- const pluginWebUrl = `${webBase}/next/plugins/${encodeURIComponent(
5400
- entry.name
5401
- )}`;
5402
- const skillEntries = Array.isArray(detail.skills) && detail.skills.length > 0 ? detail.skills.filter(
5403
- (s) => !!s && typeof s.name === "string" && s.name.length > 0
5404
- ) : [];
5405
- const skillsPlaceholders = [];
5406
- for (const s of skillEntries) {
5407
- const url = `${webBase}/next/skills/${encodeURIComponent(s.name)}`;
5408
- const mdUrl = `${url}.md`;
5409
- let rawSkillMd;
5410
- try {
5411
- const headers = {
5412
- Accept: "text/markdown,text/plain,*/*"
5413
- };
5414
- if (token) headers.Authorization = `Bearer ${token}`;
5415
- const res = await fetchFn(mdUrl, { headers });
5416
- if (res.ok) {
5417
- const text = await res.text();
5418
- if (text.length > 0) rawSkillMd = text;
5419
- }
5420
- } catch {
5421
- }
5422
- skillsPlaceholders.push({
5423
- name: s.name,
5424
- url,
5425
- ...rawSkillMd !== void 0 ? { rawSkillMd } : {}
5426
- });
5427
- }
5428
- return {
5429
- ...manifest !== void 0 ? { manifest } : {},
5430
- ...hasMcp2 ? { mcpServers: detail.mcpServers } : {},
5431
- ...skillsPlaceholders.length > 0 ? { skillsPlaceholders } : {},
5432
- pluginWebUrl
5433
- };
5434
- } catch {
5435
- return {};
5436
- }
5437
- };
5438
- }
5439
- function inferWebBase(host) {
5440
- const hostname = host.split(":")[0];
5441
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0") {
5442
- return `http://${host}`;
5443
- }
5444
- return `https://${host}`;
5445
- }
5446
- function buildLocalCachePluginContentLoader(resolved) {
5447
- return async (entry) => {
5448
- const relPath = extractLocalPluginPath(entry);
5449
- if (relPath === null) return {};
5450
- const pluginDir = pathResolve2(resolved.rootDir, relPath);
5451
- const root = pathResolve2(resolved.rootDir);
5452
- const rootWithSep = root.endsWith(sep) ? root : root + sep;
5453
- const safe = pluginDir === root || pluginDir.startsWith(rootWithSep);
5454
- if (!safe) return {};
5455
- let manifest;
5456
- let mcpServers;
5457
- let skillsSourceDir;
5458
- try {
5459
- const pj = await readFile7(
5460
- pathResolve2(pluginDir, ".claude-plugin", "plugin.json"),
5461
- "utf8"
5462
- );
5463
- const parsed = JSON.parse(pj);
5464
- manifest = pickManifestFields(parsed);
5465
- const inline = parsed.mcpServers;
5466
- if (inline && typeof inline === "object" && !Array.isArray(inline) && Object.keys(inline).length > 0) {
5467
- mcpServers = inline;
5468
- }
5469
- } catch {
5470
- }
5471
- try {
5472
- const raw = await readFile7(pathResolve2(pluginDir, ".mcp.json"), "utf8");
5473
- const parsed = JSON.parse(raw);
5474
- let servers = null;
5475
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && "mcpServers" in parsed && typeof parsed.mcpServers === "object") {
5476
- servers = parsed.mcpServers;
5477
- } else if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
5478
- servers = parsed;
5479
- }
5480
- if (servers && Object.keys(servers).length > 0) {
5481
- mcpServers = servers;
5482
- }
5483
- } catch {
5484
- }
5485
- const skillsDir = pathResolve2(pluginDir, "skills");
5486
- if (await isDir3(skillsDir)) {
5487
- skillsSourceDir = skillsDir;
5488
- }
5489
- return {
5490
- ...manifest !== void 0 ? { manifest } : {},
5491
- ...mcpServers !== void 0 ? { mcpServers } : {},
5492
- ...skillsSourceDir !== void 0 ? { skillsSourceDir } : {}
5493
- };
5494
- };
5495
- }
5496
- function extractLocalPluginPath(entry) {
5497
- if (typeof entry.source === "string") {
5498
- return entry.source;
5499
- }
5500
- if (entry.source && typeof entry.source === "object" && !Array.isArray(entry.source)) {
5501
- const obj = entry.source;
5502
- if (typeof obj.url === "string" && obj.url.length > 0) {
5503
- return null;
5504
- }
5505
- if (typeof obj.path === "string" && obj.path.length > 0) {
5506
- return obj.path;
5507
- }
5508
- }
5509
- if (entry.source === void 0 && typeof entry.name === "string" && entry.name.length > 0) {
5510
- return `plugins/${entry.name}`;
5511
- }
5512
- return null;
5513
- }
5514
- async function isDir3(p) {
5515
- try {
5516
- const s = await stat5(p);
5517
- return s.isDirectory();
5518
- } catch {
5519
- return false;
5520
- }
5521
- }
5522
- function pickManifestFields(raw) {
5523
- const m = {};
5524
- if (typeof raw.description === "string" && raw.description.length > 0) {
5525
- m.description = raw.description;
5526
- }
5527
- if (typeof raw.version === "string" && raw.version.length > 0) {
5528
- m.version = raw.version;
5529
- }
5530
- if (typeof raw.homepage === "string" && raw.homepage.length > 0) {
5531
- m.homepage = raw.homepage;
5532
- }
5533
- if (typeof raw.license === "string" && raw.license.length > 0) {
5534
- m.license = raw.license;
5535
- }
5536
- if (Array.isArray(raw.keywords)) {
5537
- const kw = raw.keywords.filter(
5538
- (x) => typeof x === "string" && x.length > 0
5539
- );
5540
- if (kw.length > 0) m.keywords = kw;
5541
- }
5542
- if (raw.author && typeof raw.author === "object" && !Array.isArray(raw.author)) {
5543
- const a = raw.author;
5544
- const author = {};
5545
- if (typeof a.name === "string") author.name = a.name;
5546
- if (typeof a.email === "string") author.email = a.email;
5547
- if (typeof a.url === "string") author.url = a.url;
5548
- if (Object.keys(author).length > 0) m.author = author;
5549
- }
5550
- if (raw.interface && typeof raw.interface === "object" && !Array.isArray(raw.interface)) {
5551
- const iface = pickInterfaceFields(raw.interface);
5552
- if (iface !== void 0) m.interface = iface;
5553
- }
5554
- return Object.keys(m).length > 0 ? m : void 0;
5555
- }
5556
- function pickInterfaceFields(raw) {
5557
- const out = {};
5558
- const stringKeys = [
5559
- "displayName",
5560
- "shortDescription",
5561
- "longDescription",
5562
- "developerName",
5563
- "category",
5564
- "websiteURL",
5565
- "privacyPolicyURL",
5566
- "termsOfServiceURL",
5567
- "brandColor",
5568
- "composerIcon",
5569
- "logo"
5570
- ];
5571
- for (const key of stringKeys) {
5572
- const v = raw[key];
5573
- if (typeof v === "string" && v.length > 0) {
5574
- out[key] = v;
5575
- }
5576
- }
5577
- for (const key of ["capabilities", "defaultPrompt", "screenshots"]) {
5578
- const v = raw[key];
5579
- if (Array.isArray(v)) {
5580
- const arr = v.filter(
5581
- (x) => typeof x === "string" && x.length > 0
5582
- );
5583
- if (arr.length > 0) out[key] = arr;
5584
- }
5585
- }
5586
- return Object.keys(out).length > 0 ? out : void 0;
5587
- }
5588
-
5589
4257
  // src/commands/marketplace/add.ts
5590
4258
  async function runAdd(input) {
5591
4259
  let source;
@@ -5717,19 +4385,19 @@ function registerAddCommand(group, _root) {
5717
4385
  }
5718
4386
 
5719
4387
  // src/installers/registry.ts
5720
- import { randomUUID as randomUUID5 } from "crypto";
5721
- import { constants as fsConstants8 } from "fs";
4388
+ import { randomUUID as randomUUID2 } from "crypto";
4389
+ import { constants as fsConstants5 } from "fs";
5722
4390
  import {
5723
- access as access8,
5724
- mkdir as mkdir7,
5725
- readFile as readFile8,
5726
- rename as rename5,
5727
- rm as rm7,
5728
- stat as stat6,
5729
- writeFile as writeFile7
4391
+ access as access5,
4392
+ mkdir as mkdir4,
4393
+ readFile as readFile5,
4394
+ rename as rename2,
4395
+ rm as rm4,
4396
+ stat as stat2,
4397
+ writeFile as writeFile4
5730
4398
  } from "fs/promises";
5731
4399
  import { homedir as homedir6 } from "os";
5732
- import { dirname as dirname6, resolve as resolve10 } from "path";
4400
+ import { dirname as dirname4, resolve as resolve8 } from "path";
5733
4401
  var REGISTRY_RELATIVE_PATH = ".rush/plugins/registry.json";
5734
4402
  var REGISTRY_SCHEMA_VERSION = 1;
5735
4403
  var RushRegistryError = class extends Error {
@@ -5809,35 +4477,35 @@ function assertRegistryShape(raw, filePath) {
5809
4477
  );
5810
4478
  }
5811
4479
  }
5812
- async function pathExists6(p) {
4480
+ async function pathExists4(p) {
5813
4481
  try {
5814
- await access8(p, fsConstants8.F_OK);
4482
+ await access5(p, fsConstants5.F_OK);
5815
4483
  return true;
5816
4484
  } catch {
5817
4485
  return false;
5818
4486
  }
5819
4487
  }
5820
- async function atomicWrite2(filePath, content) {
5821
- await mkdir7(dirname6(filePath), { recursive: true });
5822
- const tmp = `${filePath}.${randomUUID5()}.tmp`;
4488
+ async function atomicWrite(filePath, content) {
4489
+ await mkdir4(dirname4(filePath), { recursive: true });
4490
+ const tmp = `${filePath}.${randomUUID2()}.tmp`;
5823
4491
  try {
5824
- await writeFile7(tmp, content, { encoding: "utf8", flag: "w" });
5825
- await rename5(tmp, filePath);
4492
+ await writeFile4(tmp, content, { encoding: "utf8", flag: "w" });
4493
+ await rename2(tmp, filePath);
5826
4494
  } catch (err) {
5827
- await rm7(tmp, { force: true }).catch(() => {
4495
+ await rm4(tmp, { force: true }).catch(() => {
5828
4496
  });
5829
4497
  throw err;
5830
4498
  }
5831
4499
  }
5832
4500
  async function loadFromPath(filePath) {
5833
- if (!await pathExists6(filePath)) {
4501
+ if (!await pathExists4(filePath)) {
5834
4502
  return {
5835
4503
  data: { schemaVersion: REGISTRY_SCHEMA_VERSION, plugins: {} },
5836
4504
  mtimeMs: null
5837
4505
  };
5838
4506
  }
5839
- const stats = await stat6(filePath);
5840
- const raw = await readFile8(filePath, "utf8");
4507
+ const stats = await stat2(filePath);
4508
+ const raw = await readFile5(filePath, "utf8");
5841
4509
  let parsed;
5842
4510
  try {
5843
4511
  parsed = JSON.parse(raw);
@@ -5886,7 +4554,7 @@ var RushRegistryStore = class _RushRegistryStore {
5886
4554
  */
5887
4555
  static resolvePath(opts) {
5888
4556
  const home = opts?.home ?? homedir6();
5889
- return resolve10(home, REGISTRY_RELATIVE_PATH);
4557
+ return resolve8(home, REGISTRY_RELATIVE_PATH);
5890
4558
  }
5891
4559
  /**
5892
4560
  * 读取 registry 文件。
@@ -5937,20 +4605,20 @@ var RushRegistryStore = class _RushRegistryStore {
5937
4605
  async saveWithRetry(attempt) {
5938
4606
  this.assertWriteableSchema();
5939
4607
  if (this.loadedMtimeMs === null) {
5940
- if (await pathExists6(this.filePath)) {
4608
+ if (await pathExists4(this.filePath)) {
5941
4609
  if (attempt === 0) {
5942
4610
  await this.reloadAndMerge();
5943
4611
  return this.saveWithRetry(1);
5944
4612
  }
5945
4613
  throw new RushRegistryConflictError(this.filePath);
5946
4614
  }
5947
- await atomicWrite2(this.filePath, this.serialize());
5948
- const stats2 = await stat6(this.filePath);
4615
+ await atomicWrite(this.filePath, this.serialize());
4616
+ const stats2 = await stat2(this.filePath);
5949
4617
  this.loadedMtimeMs = stats2.mtimeMs;
5950
4618
  this.deletedPluginKeys.clear();
5951
4619
  return;
5952
4620
  }
5953
- const stats = await stat6(this.filePath).catch(() => null);
4621
+ const stats = await stat2(this.filePath).catch(() => null);
5954
4622
  if (stats === null) {
5955
4623
  if (attempt === 0) {
5956
4624
  await this.reloadAndMerge();
@@ -5965,8 +4633,8 @@ var RushRegistryStore = class _RushRegistryStore {
5965
4633
  }
5966
4634
  throw new RushRegistryConflictError(this.filePath);
5967
4635
  }
5968
- await atomicWrite2(this.filePath, this.serialize());
5969
- const afterStats = await stat6(this.filePath);
4636
+ await atomicWrite(this.filePath, this.serialize());
4637
+ const afterStats = await stat2(this.filePath);
5970
4638
  this.loadedMtimeMs = afterStats.mtimeMs;
5971
4639
  this.deletedPluginKeys.clear();
5972
4640
  }
@@ -6134,7 +4802,7 @@ function registerListCommand(group, root) {
6134
4802
  }
6135
4803
 
6136
4804
  // src/commands/marketplace/remove.ts
6137
- import { rm as rm8 } from "fs/promises";
4805
+ import { rm as rm5 } from "fs/promises";
6138
4806
  import { homedir as homedir7 } from "os";
6139
4807
  async function runRemove(input) {
6140
4808
  const homeOpts = input.home !== void 0 ? { home: input.home } : {};
@@ -6170,10 +4838,10 @@ async function runRemove(input) {
6170
4838
  }
6171
4839
  async function cleanupCodexMarketplace(home, name, now) {
6172
4840
  const codexHome = codexHomeDir(home);
6173
- if (!await pathExists5(codexHome)) return "absent";
4841
+ if (!await pathExists3(codexHome)) return "absent";
6174
4842
  let didSomething = false;
6175
4843
  const cfgPath = codexConfigTomlPath(home);
6176
- if (await pathExists5(cfgPath)) {
4844
+ if (await pathExists3(cfgPath)) {
6177
4845
  try {
6178
4846
  await backupCodexConfig(cfgPath, codexConfigBackupPath(home, now()));
6179
4847
  const { data, mtimeMs } = await readCodexConfig(cfgPath);
@@ -6188,8 +4856,8 @@ async function cleanupCodexMarketplace(home, name, now) {
6188
4856
  }
6189
4857
  }
6190
4858
  const mirrorDir = codexMarketplaceDir(home, name);
6191
- if (await pathExists5(mirrorDir)) {
6192
- await rm8(mirrorDir, { recursive: true, force: true }).catch(() => {
4859
+ if (await pathExists3(mirrorDir)) {
4860
+ await rm5(mirrorDir, { recursive: true, force: true }).catch(() => {
6193
4861
  });
6194
4862
  didSomething = true;
6195
4863
  }
@@ -6566,7 +5234,7 @@ function truncate3(str, max) {
6566
5234
  function registerMcpCommand(program) {
6567
5235
  const mcp = program.command("mcp").description("MCP server and platform MCP discovery");
6568
5236
  mcp.command("serve").description("Start the MCP stdio server").action(async () => {
6569
- const { startMcpServer } = await import("./server-Y2CXHDCK.js");
5237
+ const { startMcpServer } = await import("./server-PGXKRIPY.js");
6570
5238
  await startMcpServer();
6571
5239
  });
6572
5240
  mcp.command("list").alias("ls").description("List available MCP servers on the platform").option("-c, --category <category>", "Filter by category").option("-t, --tag <tag>", "Filter by tag").option("-s, --search <query>", "Search by name or description").option("--transport <type>", "Filter by transport type (stdio|sse|http)").option(
@@ -6688,7 +5356,7 @@ function registerMcpCommand(program) {
6688
5356
  "Set credential values (e.g. --set TOKEN=xxx KEY=yyy)"
6689
5357
  ).action(async (mcpId, opts) => {
6690
5358
  const format = resolveFormat(program.opts());
6691
- const { runMcpInstall, printMcpInstallSummary } = await import("./install-IH2VPYXH.js");
5359
+ const { runMcpInstall, printMcpInstallSummary } = await import("./install-JQN4BIHX.js");
6692
5360
  let setValues;
6693
5361
  if (opts.set) {
6694
5362
  setValues = {};
@@ -6749,7 +5417,7 @@ function registerMcpCommand(program) {
6749
5417
  "Comma-separated targets: claude-desktop,claude-code (default: both)"
6750
5418
  ).action(async (mcpId, opts) => {
6751
5419
  const format = resolveFormat(program.opts());
6752
- const { runMcpUninstall, printMcpUninstallSummary } = await import("./install-IH2VPYXH.js");
5420
+ const { runMcpUninstall, printMcpUninstallSummary } = await import("./install-JQN4BIHX.js");
6753
5421
  try {
6754
5422
  const result = await runMcpUninstall({
6755
5423
  mcpId,
@@ -6783,7 +5451,7 @@ function registerMcpCommand(program) {
6783
5451
 
6784
5452
  // src/commands/playbook/index.ts
6785
5453
  import { existsSync as existsSync11, readdirSync, readFileSync as readFileSync7 } from "fs";
6786
- import { basename as basename4, resolve as resolve11 } from "path";
5454
+ import { basename as basename3, resolve as resolve9 } from "path";
6787
5455
  var PLAYBOOK_DESCRIPTION = "Print agent usage playbooks (hand-off, agent-shelf, ...) as raw markdown.";
6788
5456
  var PLAYBOOK_HELP_AFTER = `
6789
5457
  Examples:
@@ -6800,19 +5468,19 @@ function resolvePlaybooksDir() {
6800
5468
  const baseDir = import.meta.dirname ?? __dirname;
6801
5469
  if (!baseDir) return null;
6802
5470
  const candidates = [
6803
- resolve11(baseDir, "skills"),
6804
- resolve11(baseDir, "..", "skills"),
6805
- resolve11(baseDir, "..", "..", "skills"),
6806
- resolve11(baseDir, "..", "..", "..", "skills"),
6807
- resolve11(baseDir, "..", "..", "..", "..", "skills")
5471
+ resolve9(baseDir, "skills"),
5472
+ resolve9(baseDir, "..", "skills"),
5473
+ resolve9(baseDir, "..", "..", "skills"),
5474
+ resolve9(baseDir, "..", "..", "..", "skills"),
5475
+ resolve9(baseDir, "..", "..", "..", "..", "skills")
6808
5476
  ];
6809
5477
  for (const p of candidates) {
6810
- if (existsSync11(resolve11(p, ".rush-playbooks"))) return p;
5478
+ if (existsSync11(resolve9(p, ".rush-playbooks"))) return p;
6811
5479
  }
6812
5480
  return null;
6813
5481
  }
6814
5482
  function listPlaybooks(dir) {
6815
- return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename4(f, ".md")).sort();
5483
+ return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename3(f, ".md")).sort();
6816
5484
  }
6817
5485
  function getAvailablePlaybooks() {
6818
5486
  const dir = resolvePlaybooksDir();
@@ -6837,7 +5505,7 @@ function printPlaybook(name, opts = {}) {
6837
5505
  return;
6838
5506
  }
6839
5507
  const target = name ?? "README";
6840
- const file2 = resolve11(dir, `${target}.md`);
5508
+ const file2 = resolve9(dir, `${target}.md`);
6841
5509
  if (!existsSync11(file2)) {
6842
5510
  output.error(`Unknown playbook "${target}".`);
6843
5511
  output.dim(`Available: ${available.join(", ") || "(none)"}`);
@@ -6853,78 +5521,30 @@ function registerPlaybookCommand(program) {
6853
5521
  }
6854
5522
 
6855
5523
  // src/commands/plugin/install.ts
6856
- import { rm as rm14 } from "fs/promises";
6857
- import { resolve as resolve22 } from "path";
5524
+ import { rm as rm11 } from "fs/promises";
5525
+ import { homedir as homedir17 } from "os";
5526
+ import { resolve as resolve20 } from "path";
6858
5527
 
6859
5528
  // src/installers/claude-code/installer.ts
6860
- import { randomUUID as randomUUID6 } from "crypto";
5529
+ import { randomUUID as randomUUID3 } from "crypto";
6861
5530
  import {
6862
- copyFile as copyFile2,
5531
+ copyFile,
6863
5532
  lstat,
6864
- mkdir as mkdir8,
6865
- readdir as readdir4,
6866
- readFile as readFile9,
5533
+ mkdir as mkdir5,
5534
+ readdir as readdir3,
5535
+ readFile as readFile6,
6867
5536
  readlink,
6868
- rename as rename6,
6869
- rm as rm9,
6870
- stat as stat7,
5537
+ rename as rename3,
5538
+ rm as rm6,
5539
+ stat as stat3,
6871
5540
  symlink,
6872
5541
  unlink
6873
5542
  } from "fs/promises";
6874
5543
  import { homedir as homedir10 } from "os";
6875
- import { dirname as dirname7, join as join9, resolve as resolve13 } from "path";
6876
-
6877
- // src/installers/claude-code/mcp.ts
6878
- import { execFileSync as execFileSync5 } from "child_process";
6879
- import { isAbsolute as isAbsolute3 } from "path";
6880
- var RUSH_AI_PLUGIN_NAME2 = "rush";
6881
- var RUSH_AI_MARKETPLACE_NAME2 = "rush-marketplace";
6882
- var RUSH_MCP_SERVER_KEY2 = "rush";
6883
- function isRushOwnPlugin(ref) {
6884
- return ref.name === RUSH_AI_PLUGIN_NAME2 && ref.marketplace === RUSH_AI_MARKETPLACE_NAME2;
6885
- }
6886
- function normalizeClaudeMcpServers(ref, manifest, resolver = defaultRushBinaryResolver) {
6887
- const servers = manifest.mcpServers;
6888
- if (servers === void 0 || servers === null) return void 0;
6889
- if (typeof servers === "string") {
6890
- return void 0;
6891
- }
6892
- if (!isRushOwnPlugin(ref)) {
6893
- return { ...servers };
6894
- }
6895
- const rushServer = servers[RUSH_MCP_SERVER_KEY2];
6896
- if (!rushServer) {
6897
- return { ...servers };
6898
- }
6899
- const currentCommand = rushServer.command;
6900
- if (typeof currentCommand === "string" && isAbsolute3(currentCommand)) {
6901
- return { ...servers };
6902
- }
6903
- const resolved = resolver();
6904
- if (!resolved || !isAbsolute3(resolved)) {
6905
- return { ...servers };
6906
- }
6907
- return {
6908
- ...servers,
6909
- [RUSH_MCP_SERVER_KEY2]: { ...rushServer, command: resolved }
6910
- };
6911
- }
6912
- function defaultRushBinaryResolver() {
6913
- try {
6914
- const result = execFileSync5("which", ["rush-ai"], {
6915
- encoding: "utf8",
6916
- stdio: ["ignore", "pipe", "ignore"]
6917
- }).trim();
6918
- if (result.length > 0 && isAbsolute3(result)) {
6919
- return result;
6920
- }
6921
- } catch {
6922
- }
6923
- return void 0;
6924
- }
5544
+ import { dirname as dirname5, join as join9, resolve as resolve11 } from "path";
6925
5545
 
6926
5546
  // src/installers/claude-code/paths.ts
6927
- import { resolve as resolve12 } from "path";
5547
+ import { resolve as resolve10 } from "path";
6928
5548
  var CLAUDE_DIR = ".claude";
6929
5549
  var PLUGINS_SUBDIR = "plugins";
6930
5550
  var CACHE_SUBDIR = "cache";
@@ -6942,27 +5562,27 @@ var ClaudeCodePaths = class {
6942
5562
  }
6943
5563
  /** `<home>/.claude/` */
6944
5564
  get claudeDir() {
6945
- return resolve12(this.home, CLAUDE_DIR);
5565
+ return resolve10(this.home, CLAUDE_DIR);
6946
5566
  }
6947
5567
  /** `<home>/.claude/settings.json` */
6948
5568
  get settingsJson() {
6949
- return resolve12(this.claudeDir, "settings.json");
5569
+ return resolve10(this.claudeDir, "settings.json");
6950
5570
  }
6951
5571
  /** `<home>/.claude/plugins/` */
6952
5572
  get pluginsDir() {
6953
- return resolve12(this.claudeDir, PLUGINS_SUBDIR);
5573
+ return resolve10(this.claudeDir, PLUGINS_SUBDIR);
6954
5574
  }
6955
5575
  /** `<home>/.claude/plugins/known_marketplaces.json` */
6956
5576
  get knownMarketplacesJson() {
6957
- return resolve12(this.pluginsDir, "known_marketplaces.json");
5577
+ return resolve10(this.pluginsDir, "known_marketplaces.json");
6958
5578
  }
6959
5579
  /** `<home>/.claude/plugins/installed_plugins.json` */
6960
5580
  get installedPluginsJson() {
6961
- return resolve12(this.pluginsDir, "installed_plugins.json");
5581
+ return resolve10(this.pluginsDir, "installed_plugins.json");
6962
5582
  }
6963
5583
  /** `<home>/.claude/plugins/cache/` */
6964
5584
  get cacheDir() {
6965
- return resolve12(this.pluginsDir, CACHE_SUBDIR);
5585
+ return resolve10(this.pluginsDir, CACHE_SUBDIR);
6966
5586
  }
6967
5587
  /**
6968
5588
  * `<home>/.claude/plugins/marketplaces/`。
@@ -6972,34 +5592,34 @@ var ClaudeCodePaths = class {
6972
5592
  * Claude Code 会识别不到 plugin 条目(regression fix,bug #4)。
6973
5593
  */
6974
5594
  get marketplacesRootDir() {
6975
- return resolve12(this.pluginsDir, "marketplaces");
5595
+ return resolve10(this.pluginsDir, "marketplaces");
6976
5596
  }
6977
5597
  /** `<home>/.claude/plugins/marketplaces/<mkt>/` */
6978
5598
  marketplaceInstallDir(marketplace) {
6979
- return resolve12(this.marketplacesRootDir, marketplace);
5599
+ return resolve10(this.marketplacesRootDir, marketplace);
6980
5600
  }
6981
5601
  /** `<home>/.claude/plugins/cache/<mkt>/` */
6982
5602
  marketplaceCacheDir(marketplace) {
6983
- return resolve12(this.cacheDir, marketplace);
5603
+ return resolve10(this.cacheDir, marketplace);
6984
5604
  }
6985
5605
  /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/` */
6986
5606
  pluginCacheDir(ref) {
6987
- return resolve12(this.marketplaceCacheDir(ref.marketplace), ref.name);
5607
+ return resolve10(this.marketplaceCacheDir(ref.marketplace), ref.name);
6988
5608
  }
6989
5609
  /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/` */
6990
5610
  pluginVersionDir(ref, version) {
6991
- return resolve12(this.pluginCacheDir(ref), version);
5611
+ return resolve10(this.pluginCacheDir(ref), version);
6992
5612
  }
6993
5613
  /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/.claude-plugin/plugin.json` */
6994
5614
  pluginManifestPath(ref, version) {
6995
- return resolve12(
5615
+ return resolve10(
6996
5616
  this.pluginVersionDir(ref, version),
6997
5617
  PLUGIN_MANIFEST_RELATIVE
6998
5618
  );
6999
5619
  }
7000
5620
  /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/<capability>/` */
7001
5621
  capabilityDir(ref, version, capability) {
7002
- return resolve12(this.pluginVersionDir(ref, version), capability);
5622
+ return resolve10(this.pluginVersionDir(ref, version), capability);
7003
5623
  }
7004
5624
  };
7005
5625
  function pluginKey(ref) {
@@ -7113,29 +5733,29 @@ var ClaudeCodeInstaller = class {
7113
5733
  }
7114
5734
  try {
7115
5735
  const writtenFiles = [];
7116
- await mkdir8(pluginVersionDir, { recursive: true });
5736
+ await mkdir5(pluginVersionDir, { recursive: true });
7117
5737
  writtenFiles.push(pluginVersionDir);
7118
5738
  for (const cap of CAPABILITY_DIRS) {
7119
- const srcDir = resolve13(plugin.sourceDir, cap);
5739
+ const srcDir = resolve11(plugin.sourceDir, cap);
7120
5740
  if (!await dirExists(srcDir)) continue;
7121
5741
  const dstDir = this.paths.capabilityDir(ref, version, cap);
7122
- await mkdir8(dstDir, { recursive: true });
5742
+ await mkdir5(dstDir, { recursive: true });
7123
5743
  writtenFiles.push(dstDir);
7124
5744
  const copied = await copyDirTracked(srcDir, dstDir);
7125
5745
  writtenFiles.push(...copied);
7126
5746
  }
7127
5747
  const manifestPath = this.paths.pluginManifestPath(ref, version);
7128
- const normalizedMcp = normalizeClaudeMcpServers(
5748
+ const normalizedMcp = await resolveClaudeMcpServers(
7129
5749
  ref,
7130
- plugin.manifest,
5750
+ plugin,
7131
5751
  this.rushBinaryResolver
7132
5752
  );
7133
5753
  if (normalizedMcp && Object.keys(normalizedMcp).length > 0) {
7134
- const mcpJsonPath = resolve13(pluginVersionDir, ".mcp.json");
5754
+ const mcpJsonPath = resolve11(pluginVersionDir, ".mcp.json");
7135
5755
  await writeJsonFile(mcpJsonPath, { mcpServers: normalizedMcp });
7136
5756
  writtenFiles.push(mcpJsonPath);
7137
5757
  }
7138
- const pluginJsonContent = buildPluginJson2(plugin, normalizedMcp);
5758
+ const pluginJsonContent = buildPluginJson(plugin, normalizedMcp);
7139
5759
  await writeJsonFile(manifestPath, pluginJsonContent);
7140
5760
  writtenFiles.push(manifestPath);
7141
5761
  const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];
@@ -7260,7 +5880,7 @@ var ClaudeCodeInstaller = class {
7260
5880
  files.push(mktDir);
7261
5881
  } else if (kind === "dir") {
7262
5882
  if (await pathExists(join9(mktDir, ".rush-marketplace-source.json"))) {
7263
- await rm9(mktDir, { recursive: true, force: true });
5883
+ await rm6(mktDir, { recursive: true, force: true });
7264
5884
  files.push(mktDir);
7265
5885
  }
7266
5886
  }
@@ -7331,12 +5951,12 @@ var ClaudeCodeInstaller = class {
7331
5951
  return { written: false, path: "" };
7332
5952
  }
7333
5953
  const mktDir = this.paths.marketplaceInstallDir(marketplaceName);
7334
- await mkdir8(this.paths.marketplacesRootDir, { recursive: true });
5954
+ await mkdir5(this.paths.marketplacesRootDir, { recursive: true });
7335
5955
  const kind = await inodeKind(mktDir);
7336
5956
  if (kind === "symlink") {
7337
5957
  const target = await readlink(mktDir).catch(() => null);
7338
- const resolvedTarget = target !== null ? resolve13(dirname7(mktDir), target) : null;
7339
- if (resolvedTarget && resolvedTarget === resolve13(src.rootDir)) {
5958
+ const resolvedTarget = target !== null ? resolve11(dirname5(mktDir), target) : null;
5959
+ if (resolvedTarget && resolvedTarget === resolve11(src.rootDir)) {
7340
5960
  return { written: false, path: mktDir };
7341
5961
  }
7342
5962
  throw new Error(
@@ -7369,7 +5989,7 @@ var ClaudeCodeInstaller = class {
7369
5989
  return { written: true, path: mktDir };
7370
5990
  }
7371
5991
  await copyDirTracked(src.rootDir, mktDir).catch(async (err) => {
7372
- await rm9(mktDir, { recursive: true, force: true }).catch(() => {
5992
+ await rm6(mktDir, { recursive: true, force: true }).catch(() => {
7373
5993
  });
7374
5994
  throw err;
7375
5995
  });
@@ -7405,7 +6025,7 @@ var ClaudeCodeInstaller = class {
7405
6025
  const pluginVersionDir = this.paths.pluginVersionDir(ref, version);
7406
6026
  const dryFiles = [pluginVersionDir];
7407
6027
  for (const cap of CAPABILITY_DIRS) {
7408
- const srcDir = resolve13(plugin.sourceDir, cap);
6028
+ const srcDir = resolve11(plugin.sourceDir, cap);
7409
6029
  if (!await dirExists(srcDir)) continue;
7410
6030
  const dstDir = this.paths.capabilityDir(ref, version, cap);
7411
6031
  dryFiles.push(dstDir);
@@ -7413,13 +6033,13 @@ var ClaudeCodeInstaller = class {
7413
6033
  dryFiles.push(...plannedFiles);
7414
6034
  }
7415
6035
  dryFiles.push(this.paths.pluginManifestPath(ref, version));
7416
- const normalizedMcpDry = normalizeClaudeMcpServers(
6036
+ const normalizedMcpDry = await resolveClaudeMcpServers(
7417
6037
  ref,
7418
- plugin.manifest,
6038
+ plugin,
7419
6039
  this.rushBinaryResolver
7420
6040
  );
7421
6041
  if (normalizedMcpDry && Object.keys(normalizedMcpDry).length > 0) {
7422
- dryFiles.push(resolve13(pluginVersionDir, ".mcp.json"));
6042
+ dryFiles.push(resolve11(pluginVersionDir, ".mcp.json"));
7423
6043
  }
7424
6044
  if (this.marketplaceSource) {
7425
6045
  const mktInstallDir = this.paths.marketplaceInstallDir(ref.marketplace);
@@ -7436,9 +6056,9 @@ var ClaudeCodeInstaller = class {
7436
6056
  if (await this.wouldSettingsChange(pluginKey(ref))) {
7437
6057
  dryFiles.push(this.paths.settingsJson);
7438
6058
  }
7439
- const normalizedMcp = normalizeClaudeMcpServers(
6059
+ const normalizedMcp = await resolveClaudeMcpServers(
7440
6060
  ref,
7441
- plugin.manifest,
6061
+ plugin,
7442
6062
  this.rushBinaryResolver
7443
6063
  );
7444
6064
  const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];
@@ -7533,8 +6153,8 @@ var ClaudeCodeInstaller = class {
7533
6153
  const settings = await this.readRawJsonForRollback(this.paths.settingsJson);
7534
6154
  let backupVersionDir = null;
7535
6155
  if (preexistedVersion === "dir") {
7536
- backupVersionDir = `${pluginVersionDir}.rollback-${randomUUID6()}`;
7537
- await rename6(pluginVersionDir, backupVersionDir);
6156
+ backupVersionDir = `${pluginVersionDir}.rollback-${randomUUID3()}`;
6157
+ await rename3(pluginVersionDir, backupVersionDir);
7538
6158
  }
7539
6159
  return {
7540
6160
  ref,
@@ -7555,7 +6175,7 @@ var ClaudeCodeInstaller = class {
7555
6175
  */
7556
6176
  async finalizeRollback(snap) {
7557
6177
  if (snap.backupVersionDir) {
7558
- await rm9(snap.backupVersionDir, { recursive: true, force: true });
6178
+ await rm6(snap.backupVersionDir, { recursive: true, force: true });
7559
6179
  }
7560
6180
  }
7561
6181
  /**
@@ -7568,19 +6188,19 @@ var ClaudeCodeInstaller = class {
7568
6188
  */
7569
6189
  async applyRollback(snap) {
7570
6190
  if (snap.preexistedVersion === "dir" && snap.backupVersionDir) {
7571
- await rm9(snap.pluginVersionDir, { recursive: true, force: true });
7572
- await rename6(snap.backupVersionDir, snap.pluginVersionDir);
6191
+ await rm6(snap.pluginVersionDir, { recursive: true, force: true });
6192
+ await rename3(snap.backupVersionDir, snap.pluginVersionDir);
7573
6193
  } else if (snap.preexistedVersion === "missing") {
7574
- await rm9(snap.pluginVersionDir, { recursive: true, force: true });
6194
+ await rm6(snap.pluginVersionDir, { recursive: true, force: true });
7575
6195
  }
7576
6196
  if (snap.preexistedPlugin === "missing") {
7577
- await rm9(this.paths.pluginCacheDir(snap.ref), {
6197
+ await rm6(this.paths.pluginCacheDir(snap.ref), {
7578
6198
  recursive: true,
7579
6199
  force: true
7580
6200
  });
7581
6201
  }
7582
6202
  if (snap.preexistedMarketplace === "missing") {
7583
- await rm9(this.paths.marketplaceCacheDir(snap.ref.marketplace), {
6203
+ await rm6(this.paths.marketplaceCacheDir(snap.ref.marketplace), {
7584
6204
  recursive: true,
7585
6205
  force: true
7586
6206
  });
@@ -7594,7 +6214,7 @@ var ClaudeCodeInstaller = class {
7594
6214
  await unlink(mktInstallDir).catch(() => {
7595
6215
  });
7596
6216
  } else if (currentKind !== "missing") {
7597
- await rm9(mktInstallDir, { recursive: true, force: true }).catch(
6217
+ await rm6(mktInstallDir, { recursive: true, force: true }).catch(
7598
6218
  () => {
7599
6219
  }
7600
6220
  );
@@ -7617,7 +6237,7 @@ var ClaudeCodeInstaller = class {
7617
6237
  async restoreRawJson(path4, state) {
7618
6238
  if (!state.existed) {
7619
6239
  if (await pathExists(path4)) {
7620
- await rm9(path4, { force: true });
6240
+ await rm6(path4, { force: true });
7621
6241
  }
7622
6242
  return;
7623
6243
  }
@@ -7869,7 +6489,7 @@ var ClaudeCodeInstaller = class {
7869
6489
  );
7870
6490
  const servers = data.mcpServers;
7871
6491
  if (typeof servers === "string") {
7872
- const mcpJsonPath = resolve13(dirname7(manifestPath), "..", servers);
6492
+ const mcpJsonPath = resolve11(dirname5(manifestPath), "..", servers);
7873
6493
  if (!await pathExists(mcpJsonPath)) return [];
7874
6494
  const { data: mcpData } = await readJsonFile(
7875
6495
  mcpJsonPath,
@@ -7914,7 +6534,24 @@ function parsePluginKey2(key) {
7914
6534
  if (at <= 0 || at === key.length - 1) return null;
7915
6535
  return { name: key.slice(0, at), marketplace: key.slice(at + 1) };
7916
6536
  }
7917
- function buildPluginJson2(plugin, mcpServers) {
6537
+ async function resolveClaudeMcpServers(ref, plugin, rushBinaryResolver) {
6538
+ const decl = plugin.manifest.mcpServers;
6539
+ if (decl === void 0 || decl === null) return void 0;
6540
+ if (typeof decl === "object") {
6541
+ return normalizeClaudeMcpServers(ref, plugin.manifest, rushBinaryResolver);
6542
+ }
6543
+ if (typeof decl === "string") {
6544
+ const external = await readExternalMcpServers(plugin.sourceDir, decl);
6545
+ if (!external || Object.keys(external).length === 0) return void 0;
6546
+ return normalizeClaudeMcpServers(
6547
+ ref,
6548
+ { ...plugin.manifest, mcpServers: external },
6549
+ rushBinaryResolver
6550
+ );
6551
+ }
6552
+ return void 0;
6553
+ }
6554
+ function buildPluginJson(plugin, mcpServers) {
7918
6555
  const { manifest } = plugin;
7919
6556
  const {
7920
6557
  mcpServers: _origMcp,
@@ -7930,7 +6567,7 @@ function buildPluginJson2(plugin, mcpServers) {
7930
6567
  }
7931
6568
  async function readMarketplaceCacheSource(metaPath) {
7932
6569
  try {
7933
- const raw = await readFile9(metaPath, "utf8");
6570
+ const raw = await readFile6(metaPath, "utf8");
7934
6571
  const parsed = JSON.parse(raw);
7935
6572
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7936
6573
  const sourceRaw = parsed.sourceRaw;
@@ -7965,7 +6602,7 @@ async function defaultSymlinkRunner(target, linkPath) {
7965
6602
  }
7966
6603
  async function dirExists(p) {
7967
6604
  try {
7968
- const s = await stat7(p);
6605
+ const s = await stat3(p);
7969
6606
  return s.isDirectory();
7970
6607
  } catch {
7971
6608
  return false;
@@ -7984,7 +6621,7 @@ async function inodeKind(p) {
7984
6621
  }
7985
6622
  async function listDirFilesRecursive(srcDir, dstDir) {
7986
6623
  const planned = [];
7987
- const entries = await readdir4(srcDir, { withFileTypes: true });
6624
+ const entries = await readdir3(srcDir, { withFileTypes: true });
7988
6625
  for (const entry of entries) {
7989
6626
  const srcPath = join9(srcDir, entry.name);
7990
6627
  const dstPath = join9(dstDir, entry.name);
@@ -8000,18 +6637,18 @@ async function listDirFilesRecursive(srcDir, dstDir) {
8000
6637
  }
8001
6638
  async function copyDirTracked(srcDir, dstDir) {
8002
6639
  const written = [];
8003
- const entries = await readdir4(srcDir, { withFileTypes: true });
6640
+ const entries = await readdir3(srcDir, { withFileTypes: true });
8004
6641
  for (const entry of entries) {
8005
6642
  const srcPath = join9(srcDir, entry.name);
8006
6643
  const dstPath = join9(dstDir, entry.name);
8007
6644
  if (entry.isDirectory()) {
8008
- await mkdir8(dstPath, { recursive: true });
6645
+ await mkdir5(dstPath, { recursive: true });
8009
6646
  written.push(dstPath);
8010
6647
  const subWritten = await copyDirTracked(srcPath, dstPath);
8011
6648
  written.push(...subWritten);
8012
6649
  } else if (entry.isFile()) {
8013
- await mkdir8(dirname7(dstPath), { recursive: true });
8014
- await copyFile2(srcPath, dstPath);
6650
+ await mkdir5(dirname5(dstPath), { recursive: true });
6651
+ await copyFile(srcPath, dstPath);
8015
6652
  written.push(dstPath);
8016
6653
  }
8017
6654
  }
@@ -8021,7 +6658,7 @@ async function writeAtomicWithMtimeCheck(filePath, data, expectedMtimeMs) {
8021
6658
  if (expectedMtimeMs !== null) {
8022
6659
  let currentMtime = null;
8023
6660
  try {
8024
- const s = await stat7(filePath);
6661
+ const s = await stat3(filePath);
8025
6662
  currentMtime = s.mtimeMs;
8026
6663
  } catch {
8027
6664
  currentMtime = null;
@@ -8044,20 +6681,20 @@ async function retryOnConflict(fn) {
8044
6681
  }
8045
6682
 
8046
6683
  // src/installers/cursor/installer.ts
8047
- import { randomUUID as randomUUID8 } from "crypto";
8048
- import { constants as fsConstants10 } from "fs";
6684
+ import { randomUUID as randomUUID5 } from "crypto";
6685
+ import { constants as fsConstants7 } from "fs";
8049
6686
  import {
8050
- access as access10,
8051
- cp as cp3,
8052
- mkdir as mkdir10,
8053
- readFile as readFile12,
8054
- rename as rename8,
8055
- rm as rm11,
8056
- stat as stat9,
8057
- writeFile as writeFile9
6687
+ access as access7,
6688
+ cp as cp2,
6689
+ mkdir as mkdir7,
6690
+ readFile as readFile9,
6691
+ rename as rename5,
6692
+ rm as rm8,
6693
+ stat as stat5,
6694
+ writeFile as writeFile6
8058
6695
  } from "fs/promises";
8059
6696
  import { homedir as homedir11 } from "os";
8060
- import { dirname as dirname9, relative, resolve as resolve16 } from "path";
6697
+ import { dirname as dirname7, relative, resolve as resolve14 } from "path";
8061
6698
 
8062
6699
  // src/installers/cursor/marker.ts
8063
6700
  var RUSH_AI_MARKER = "<!-- rush-ai:auto-generated -->";
@@ -8066,27 +6703,27 @@ function hasRushAiMarker(fileContent) {
8066
6703
  }
8067
6704
 
8068
6705
  // src/installers/cursor/mcp.ts
8069
- import { randomUUID as randomUUID7 } from "crypto";
8070
- import { constants as fsConstants9 } from "fs";
6706
+ import { randomUUID as randomUUID4 } from "crypto";
6707
+ import { constants as fsConstants6 } from "fs";
8071
6708
  import {
8072
- access as access9,
8073
- mkdir as mkdir9,
8074
- readFile as readFile10,
8075
- rename as rename7,
8076
- rm as rm10,
8077
- stat as stat8,
8078
- writeFile as writeFile8
6709
+ access as access6,
6710
+ mkdir as mkdir6,
6711
+ readFile as readFile7,
6712
+ rename as rename4,
6713
+ rm as rm7,
6714
+ stat as stat4,
6715
+ writeFile as writeFile5
8079
6716
  } from "fs/promises";
8080
- import { dirname as dirname8 } from "path";
6717
+ import { dirname as dirname6 } from "path";
8081
6718
  async function readCursorMcpJson(filePath) {
8082
6719
  return (await readCursorMcpJsonWithMtime(filePath)).data;
8083
6720
  }
8084
6721
  async function readCursorMcpJsonWithMtime(filePath) {
8085
- if (!await pathExists7(filePath)) {
6722
+ if (!await pathExists5(filePath)) {
8086
6723
  return { data: {}, mtimeMs: null };
8087
6724
  }
8088
- const stats = await stat8(filePath);
8089
- const raw = await readFile10(filePath, "utf8");
6725
+ const stats = await stat4(filePath);
6726
+ const raw = await readFile7(filePath, "utf8");
8090
6727
  let parsed;
8091
6728
  try {
8092
6729
  parsed = JSON.parse(raw);
@@ -8153,22 +6790,22 @@ function removeMcpServers(current, keysToRemove) {
8153
6790
  return cloned;
8154
6791
  }
8155
6792
  async function writeCursorMcpJson(filePath, data) {
8156
- await mkdir9(dirname8(filePath), { recursive: true });
6793
+ await mkdir6(dirname6(filePath), { recursive: true });
8157
6794
  const content = `${JSON.stringify(data, null, 2)}
8158
6795
  `;
8159
- const tmp = `${filePath}.${randomUUID7()}.tmp`;
6796
+ const tmp = `${filePath}.${randomUUID4()}.tmp`;
8160
6797
  try {
8161
- await writeFile8(tmp, content, { encoding: "utf8", flag: "w" });
8162
- await rename7(tmp, filePath);
6798
+ await writeFile5(tmp, content, { encoding: "utf8", flag: "w" });
6799
+ await rename4(tmp, filePath);
8163
6800
  } catch (err) {
8164
- await rm10(tmp, { force: true }).catch(() => {
6801
+ await rm7(tmp, { force: true }).catch(() => {
8165
6802
  });
8166
6803
  throw err;
8167
6804
  }
8168
6805
  }
8169
- async function pathExists7(p) {
6806
+ async function pathExists5(p) {
8170
6807
  try {
8171
- await access9(p, fsConstants9.F_OK);
6808
+ await access6(p, fsConstants6.F_OK);
8172
6809
  return true;
8173
6810
  } catch {
8174
6811
  return false;
@@ -8176,25 +6813,25 @@ async function pathExists7(p) {
8176
6813
  }
8177
6814
 
8178
6815
  // src/installers/cursor/paths.ts
8179
- import { resolve as resolve14 } from "path";
6816
+ import { resolve as resolve12 } from "path";
8180
6817
  var CURSOR_RELATIVE_DIR = ".cursor";
8181
6818
  function cursorDir(home) {
8182
- return resolve14(home, CURSOR_RELATIVE_DIR);
6819
+ return resolve12(home, CURSOR_RELATIVE_DIR);
8183
6820
  }
8184
6821
  function cursorMcpJsonPath(home) {
8185
- return resolve14(cursorDir(home), "mcp.json");
6822
+ return resolve12(cursorDir(home), "mcp.json");
8186
6823
  }
8187
6824
  function cursorRulesDir(home) {
8188
- return resolve14(cursorDir(home), "rules");
6825
+ return resolve12(cursorDir(home), "rules");
8189
6826
  }
8190
6827
  function cursorRuleMdcPath(home, ruleName) {
8191
- return resolve14(cursorRulesDir(home), `${ruleName}.mdc`);
6828
+ return resolve12(cursorRulesDir(home), `${ruleName}.mdc`);
8192
6829
  }
8193
6830
  function cursorSkillsDir(home) {
8194
- return resolve14(cursorDir(home), "skills");
6831
+ return resolve12(cursorDir(home), "skills");
8195
6832
  }
8196
6833
  function cursorSkillDir(home, skillName) {
8197
- return resolve14(cursorSkillsDir(home), skillName);
6834
+ return resolve12(cursorSkillsDir(home), skillName);
8198
6835
  }
8199
6836
  function bridgeSkillFileReference(skillName) {
8200
6837
  return `.cursor/skills/${skillName}/SKILL.md`;
@@ -8304,13 +6941,13 @@ function quoteIfNeeded(value) {
8304
6941
  }
8305
6942
 
8306
6943
  // src/installers/cursor/skills.ts
8307
- import { readFile as readFile11 } from "fs/promises";
8308
- import { resolve as resolve15 } from "path";
6944
+ import { readFile as readFile8 } from "fs/promises";
6945
+ import { resolve as resolve13 } from "path";
8309
6946
  async function readSkillDescription(skillSourceDir) {
8310
- const skillMdPath = resolve15(skillSourceDir, "SKILL.md");
6947
+ const skillMdPath = resolve13(skillSourceDir, "SKILL.md");
8311
6948
  let raw;
8312
6949
  try {
8313
- raw = await readFile11(skillMdPath, "utf8");
6950
+ raw = await readFile8(skillMdPath, "utf8");
8314
6951
  } catch {
8315
6952
  return void 0;
8316
6953
  }
@@ -8340,11 +6977,11 @@ async function executeRollback(ledger) {
8340
6977
  if (entry.kind === "restore-file") {
8341
6978
  await atomicWriteFile(entry.path, entry.originalContent);
8342
6979
  } else if (entry.kind === "remove-file") {
8343
- await rm11(entry.path, { force: true });
6980
+ await rm8(entry.path, { force: true });
8344
6981
  } else if (entry.kind === "remove-dir") {
8345
- await rm11(entry.path, { recursive: true, force: true });
6982
+ await rm8(entry.path, { recursive: true, force: true });
8346
6983
  } else {
8347
- await rm11(entry.path, { recursive: true, force: true });
6984
+ await rm8(entry.path, { recursive: true, force: true });
8348
6985
  await safeRename(entry.backupPath, entry.path);
8349
6986
  }
8350
6987
  } catch (err) {
@@ -8356,20 +6993,20 @@ async function executeRollback(ledger) {
8356
6993
  return errors;
8357
6994
  }
8358
6995
  async function atomicWriteFile(filePath, content) {
8359
- await mkdir10(dirname9(filePath), { recursive: true });
8360
- const tmp = `${filePath}.${randomUUID8()}.tmp`;
6996
+ await mkdir7(dirname7(filePath), { recursive: true });
6997
+ const tmp = `${filePath}.${randomUUID5()}.tmp`;
8361
6998
  try {
8362
- await writeFile9(tmp, content, { encoding: "utf8", flag: "w" });
8363
- await rename8(tmp, filePath);
6999
+ await writeFile6(tmp, content, { encoding: "utf8", flag: "w" });
7000
+ await rename5(tmp, filePath);
8364
7001
  } catch (err) {
8365
- await rm11(tmp, { force: true }).catch(() => {
7002
+ await rm8(tmp, { force: true }).catch(() => {
8366
7003
  });
8367
7004
  throw err;
8368
7005
  }
8369
7006
  }
8370
7007
  async function getMcpMtimeMs(filePath) {
8371
7008
  try {
8372
- const s = await stat9(filePath);
7009
+ const s = await stat5(filePath);
8373
7010
  return s.mtimeMs;
8374
7011
  } catch {
8375
7012
  return null;
@@ -8602,13 +7239,13 @@ var CursorInstaller = class {
8602
7239
  if (info === "missing") continue;
8603
7240
  if (info === "skill-dir") {
8604
7241
  if (!dryRun) {
8605
- await rm11(filePath, { recursive: true, force: true });
7242
+ await rm8(filePath, { recursive: true, force: true });
8606
7243
  }
8607
7244
  removedFiles.push(filePath);
8608
7245
  continue;
8609
7246
  }
8610
7247
  if (info === "mdc-file") {
8611
- const content = await readFile12(filePath, "utf8").catch(() => "");
7248
+ const content = await readFile9(filePath, "utf8").catch(() => "");
8612
7249
  if (!hasRushAiMarker(content)) {
8613
7250
  warnings.push(
8614
7251
  `${filePath} \u5DF2\u88AB\u4FEE\u6539\uFF08marker \u4E22\u5931\uFF09\uFF0C\u8DF3\u8FC7\u4E0D\u52A8\u2014\u2014\u8BF7\u7528\u6237\u624B\u52A8\u5904\u7406`
@@ -8616,13 +7253,13 @@ var CursorInstaller = class {
8616
7253
  continue;
8617
7254
  }
8618
7255
  if (!dryRun) {
8619
- await rm11(filePath, { force: true });
7256
+ await rm8(filePath, { force: true });
8620
7257
  }
8621
7258
  removedFiles.push(filePath);
8622
7259
  continue;
8623
7260
  }
8624
7261
  if (!dryRun) {
8625
- await rm11(filePath, { force: true });
7262
+ await rm8(filePath, { force: true });
8626
7263
  }
8627
7264
  removedFiles.push(filePath);
8628
7265
  }
@@ -8670,10 +7307,10 @@ var CursorInstaller = class {
8670
7307
  const prevEntry = registryStore.get(plugin.ref);
8671
7308
  const prevFiles = new Set(prevEntry?.targets?.cursor?.files ?? []);
8672
7309
  if (plugin.capabilities.includes("skills")) {
8673
- const skillsSourceDir = resolve16(plugin.sourceDir, "skills");
7310
+ const skillsSourceDir = resolve14(plugin.sourceDir, "skills");
8674
7311
  const skillDirs = await listSkillDirs(skillsSourceDir);
8675
7312
  for (const skillName of skillDirs) {
8676
- const srcDir = resolve16(skillsSourceDir, skillName);
7313
+ const srcDir = resolve14(skillsSourceDir, skillName);
8677
7314
  const destDir = cursorSkillDir(this.home, skillName);
8678
7315
  const bridgeRulePath = cursorRuleMdcPath(this.home, skillName);
8679
7316
  if (await dirExists2(destDir)) {
@@ -8705,10 +7342,10 @@ var CursorInstaller = class {
8705
7342
  }
8706
7343
  }
8707
7344
  if (plugin.capabilities.includes("rules")) {
8708
- const rulesSourceDir = resolve16(plugin.sourceDir, "rules");
7345
+ const rulesSourceDir = resolve14(plugin.sourceDir, "rules");
8709
7346
  const rulesFiles = await listRuleMdFiles(rulesSourceDir);
8710
7347
  for (const ruleFile of rulesFiles) {
8711
- const srcPath = resolve16(rulesSourceDir, ruleFile);
7348
+ const srcPath = resolve14(rulesSourceDir, ruleFile);
8712
7349
  const ruleName = ruleFile.replace(/\.md$/i, "");
8713
7350
  const destPath = cursorRuleMdcPath(this.home, ruleName);
8714
7351
  await assertMdcWritable(destPath, args.force);
@@ -8717,7 +7354,7 @@ var CursorInstaller = class {
8717
7354
  `rule "${ruleName}" \u4E0E skill "${ruleName}" \u7684 bridge rule \u540C\u540D\uFF0Crule \u5185\u5BB9\u4F1A\u8986\u76D6 bridge rule`
8718
7355
  );
8719
7356
  }
8720
- const src = await readFile12(srcPath, "utf8");
7357
+ const src = await readFile9(srcPath, "utf8");
8721
7358
  const parsed = parseMarkdown(src);
8722
7359
  const content = renderMdc(parsed, {
8723
7360
  descriptionFallback: plugin.manifest.description ?? ruleName
@@ -8726,7 +7363,7 @@ var CursorInstaller = class {
8726
7363
  plannedFiles.push(destPath);
8727
7364
  }
8728
7365
  }
8729
- const mcpAdditions = extractMcpAdditions(plugin);
7366
+ const mcpAdditions = await extractMcpAdditions(plugin);
8730
7367
  if (mcpAdditions && Object.keys(mcpAdditions).length > 0) {
8731
7368
  plannedFiles.push(cursorMcpJsonPath(this.home));
8732
7369
  plannedMcpKeys.push(...Object.keys(mcpAdditions));
@@ -8745,7 +7382,7 @@ var CursorInstaller = class {
8745
7382
  // -------------------------------------------------------------------------
8746
7383
  async writeSkillDir(args) {
8747
7384
  const preExisted = await dirExists2(args.destDir);
8748
- await mkdir10(dirname9(args.destDir), { recursive: true });
7385
+ await mkdir7(dirname7(args.destDir), { recursive: true });
8749
7386
  if (preExisted) {
8750
7387
  const backup = `${args.destDir}.${process.pid}.${Date.now()}.bak`;
8751
7388
  await safeRename(args.destDir, backup);
@@ -8757,12 +7394,12 @@ var CursorInstaller = class {
8757
7394
  } else {
8758
7395
  args.ledger.push({ kind: "remove-dir", path: args.destDir });
8759
7396
  }
8760
- await cp3(args.srcDir, args.destDir, { recursive: true });
7397
+ await cp2(args.srcDir, args.destDir, { recursive: true });
8761
7398
  }
8762
7399
  async writeMdcFile(args) {
8763
7400
  const preExisted = await fileExists(args.path);
8764
7401
  if (preExisted) {
8765
- const original = await readFile12(args.path, "utf8");
7402
+ const original = await readFile9(args.path, "utf8");
8766
7403
  args.ledger.push({
8767
7404
  kind: "restore-file",
8768
7405
  path: args.path,
@@ -8776,7 +7413,7 @@ var CursorInstaller = class {
8776
7413
  async writeMcpMerge(args) {
8777
7414
  const preExisted = await fileExists(args.mcpPath);
8778
7415
  if (preExisted) {
8779
- const original = await readFile12(args.mcpPath, "utf8");
7416
+ const original = await readFile9(args.mcpPath, "utf8");
8780
7417
  args.ledger.push({
8781
7418
  kind: "restore-file",
8782
7419
  path: args.mcpPath,
@@ -8860,7 +7497,7 @@ function skippedResult() {
8860
7497
  }
8861
7498
  async function assertMdcWritable(path4, force) {
8862
7499
  if (!await fileExists(path4)) return;
8863
- const raw = await readFile12(path4, "utf8").catch(() => "");
7500
+ const raw = await readFile9(path4, "utf8").catch(() => "");
8864
7501
  if (hasRushAiMarker(raw)) return;
8865
7502
  if (force) return;
8866
7503
  throw new CursorInstallConflictError(
@@ -8868,12 +7505,19 @@ async function assertMdcWritable(path4, force) {
8868
7505
  `\u76EE\u6807 .mdc \u5DF2\u5B58\u5728\u4E14\u65E0 rush-ai marker\uFF08${path4}\uFF09\u2014\u2014\u4E0D\u8986\u76D6\u7528\u6237\u624B\u5199 rule`
8869
7506
  );
8870
7507
  }
8871
- function extractMcpAdditions(plugin) {
7508
+ async function extractMcpAdditions(plugin) {
8872
7509
  const m = plugin.manifest.mcpServers;
8873
7510
  if (m === void 0 || m === null) return null;
8874
- if (typeof m === "string") return null;
7511
+ let serverMap = null;
7512
+ if (typeof m === "object") {
7513
+ serverMap = m;
7514
+ } else if (typeof m === "string") {
7515
+ const { readExternalMcpServers: readExternalMcpServers2 } = await import("./mcp-UZYID3GG.js");
7516
+ serverMap = await readExternalMcpServers2(plugin.sourceDir, m);
7517
+ }
7518
+ if (!serverMap) return null;
8875
7519
  const out = {};
8876
- for (const [key, cfg] of Object.entries(m)) {
7520
+ for (const [key, cfg] of Object.entries(serverMap)) {
8877
7521
  if (cfg && typeof cfg === "object" && typeof cfg.command === "string") {
8878
7522
  out[key] = cfg;
8879
7523
  }
@@ -8882,19 +7526,19 @@ function extractMcpAdditions(plugin) {
8882
7526
  }
8883
7527
  async function listSkillDirs(skillsDir) {
8884
7528
  if (!await dirExists2(skillsDir)) return [];
8885
- const { readdir: readdir6 } = await import("fs/promises");
8886
- const entries = await readdir6(skillsDir, { withFileTypes: true });
7529
+ const { readdir: readdir5 } = await import("fs/promises");
7530
+ const entries = await readdir5(skillsDir, { withFileTypes: true });
8887
7531
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).filter((name) => !name.startsWith(".")).sort();
8888
7532
  }
8889
7533
  async function listRuleMdFiles(rulesDir) {
8890
7534
  if (!await dirExists2(rulesDir)) return [];
8891
- const { readdir: readdir6 } = await import("fs/promises");
8892
- const entries = await readdir6(rulesDir, { withFileTypes: true });
7535
+ const { readdir: readdir5 } = await import("fs/promises");
7536
+ const entries = await readdir5(rulesDir, { withFileTypes: true });
8893
7537
  return entries.filter((e) => e.isFile() && /\.md$/i.test(e.name)).map((e) => e.name).sort();
8894
7538
  }
8895
7539
  async function dirExists2(p) {
8896
7540
  try {
8897
- const s = await stat9(p);
7541
+ const s = await stat5(p);
8898
7542
  return s.isDirectory();
8899
7543
  } catch {
8900
7544
  return false;
@@ -8902,21 +7546,21 @@ async function dirExists2(p) {
8902
7546
  }
8903
7547
  async function fileExists(p) {
8904
7548
  try {
8905
- await access10(p, fsConstants10.F_OK);
8906
- const s = await stat9(p);
7549
+ await access7(p, fsConstants7.F_OK);
7550
+ const s = await stat5(p);
8907
7551
  return s.isFile();
8908
7552
  } catch {
8909
7553
  return false;
8910
7554
  }
8911
7555
  }
8912
7556
  async function safeRename(from, to) {
8913
- const { rename: rename10 } = await import("fs/promises");
8914
- await rename10(from, to);
7557
+ const { rename: rename7 } = await import("fs/promises");
7558
+ await rename7(from, to);
8915
7559
  }
8916
7560
  async function classifyArtifactPath(p) {
8917
7561
  let s;
8918
7562
  try {
8919
- s = await stat9(p);
7563
+ s = await stat5(p);
8920
7564
  } catch {
8921
7565
  return "missing";
8922
7566
  }
@@ -8929,22 +7573,22 @@ async function classifyArtifactPath(p) {
8929
7573
  }
8930
7574
 
8931
7575
  // src/migration/cleanup.ts
8932
- import { lstat as lstat3, rm as rm12, unlink as unlink2 } from "fs/promises";
7576
+ import { lstat as lstat3, rm as rm9, unlink as unlink2 } from "fs/promises";
8933
7577
  import { homedir as homedir13 } from "os";
8934
- import { resolve as resolve18 } from "path";
7578
+ import { resolve as resolve16 } from "path";
8935
7579
 
8936
7580
  // src/migration/detect.ts
8937
- import { access as access11, lstat as lstat2, readFile as readFile13, readlink as readlink2, stat as stat10 } from "fs/promises";
7581
+ import { access as access8, lstat as lstat2, readFile as readFile10, readlink as readlink2, stat as stat6 } from "fs/promises";
8938
7582
  import { homedir as homedir12 } from "os";
8939
- import { isAbsolute as isAbsolute4, resolve as resolve17 } from "path";
7583
+ import { isAbsolute as isAbsolute3, resolve as resolve15 } from "path";
8940
7584
  var LEGACY_ASSET_MANIFEST_RELATIVE = ".rush/plugins/claude-code/asset-manifest.json";
8941
7585
  var LEGACY_ASSETS_DIR_RELATIVE = ".rush/plugins/claude-code/assets";
8942
7586
  var LEGACY_CLAUDE_CODE_DIR_RELATIVE = ".rush/plugins/claude-code";
8943
7587
  var LEGACY_SKILL_SYMLINK_RELATIVE = ".claude/skills/rush-task";
8944
7588
  var CLAUDE_SETTINGS_JSON_RELATIVE = ".claude/settings.json";
8945
- async function pathExists8(p) {
7589
+ async function pathExists6(p) {
8946
7590
  try {
8947
- await access11(p);
7591
+ await access8(p);
8948
7592
  return true;
8949
7593
  } catch {
8950
7594
  return false;
@@ -8955,7 +7599,7 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
8955
7599
  const lst = await lstat2(linkPath);
8956
7600
  if (!lst.isSymbolicLink()) return false;
8957
7601
  const rawTarget = await readlink2(linkPath);
8958
- const resolvedTarget = isAbsolute4(rawTarget) ? rawTarget : resolve17(linkPath, "..", rawTarget);
7602
+ const resolvedTarget = isAbsolute3(rawTarget) ? rawTarget : resolve15(linkPath, "..", rawTarget);
8959
7603
  return resolvedTarget === expectedTarget || resolvedTarget.startsWith(`${expectedTarget}/`);
8960
7604
  } catch {
8961
7605
  return false;
@@ -8963,8 +7607,8 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
8963
7607
  }
8964
7608
  async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
8965
7609
  try {
8966
- if (!await pathExists8(settingsJsonPath)) return false;
8967
- const raw = await readFile13(settingsJsonPath, "utf8");
7610
+ if (!await pathExists6(settingsJsonPath)) return false;
7611
+ const raw = await readFile10(settingsJsonPath, "utf8");
8968
7612
  let parsed;
8969
7613
  try {
8970
7614
  parsed = JSON.parse(raw);
@@ -8992,12 +7636,12 @@ async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
8992
7636
  }
8993
7637
  async function detectLegacyInstall(opts = {}) {
8994
7638
  const home = opts.home ?? homedir12();
8995
- const assetManifestPath = resolve17(home, LEGACY_ASSET_MANIFEST_RELATIVE);
8996
- const legacyAssetsDir = resolve17(home, LEGACY_ASSETS_DIR_RELATIVE);
8997
- const skillSymlinkPath = resolve17(home, LEGACY_SKILL_SYMLINK_RELATIVE);
8998
- const settingsJsonPath = resolve17(home, CLAUDE_SETTINGS_JSON_RELATIVE);
7639
+ const assetManifestPath = resolve15(home, LEGACY_ASSET_MANIFEST_RELATIVE);
7640
+ const legacyAssetsDir = resolve15(home, LEGACY_ASSETS_DIR_RELATIVE);
7641
+ const skillSymlinkPath = resolve15(home, LEGACY_SKILL_SYMLINK_RELATIVE);
7642
+ const settingsJsonPath = resolve15(home, CLAUDE_SETTINGS_JSON_RELATIVE);
8999
7643
  const [hasAssetManifest, hasLegacySkillSymlink, legacyMcpWithoutEnabled] = await Promise.all([
9000
- pathExists8(assetManifestPath),
7644
+ pathExists6(assetManifestPath),
9001
7645
  isSymlinkPointingInto(skillSymlinkPath, legacyAssetsDir),
9002
7646
  hasLegacyMcpWithoutEnabled(settingsJsonPath)
9003
7647
  ]);
@@ -9010,11 +7654,11 @@ async function detectLegacyInstall(opts = {}) {
9010
7654
  }
9011
7655
  async function detectLegacyVersion(opts = {}) {
9012
7656
  const home = opts.home ?? homedir12();
9013
- const manifestPath = resolve17(home, LEGACY_ASSET_MANIFEST_RELATIVE);
7657
+ const manifestPath = resolve15(home, LEGACY_ASSET_MANIFEST_RELATIVE);
9014
7658
  try {
9015
- const lst = await stat10(manifestPath);
7659
+ const lst = await stat6(manifestPath);
9016
7660
  if (!lst.isFile()) return "0.7.x";
9017
- const raw = await readFile13(manifestPath, "utf8");
7661
+ const raw = await readFile10(manifestPath, "utf8");
9018
7662
  const parsed = JSON.parse(raw);
9019
7663
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
9020
7664
  const v = parsed.version;
@@ -9028,9 +7672,9 @@ async function detectLegacyVersion(opts = {}) {
9028
7672
 
9029
7673
  // src/migration/cleanup.ts
9030
7674
  async function cleanupLegacyDir(home) {
9031
- const dir = resolve18(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
7675
+ const dir = resolve16(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
9032
7676
  try {
9033
- await rm12(dir, { recursive: true, force: true });
7677
+ await rm9(dir, { recursive: true, force: true });
9034
7678
  return { ok: true };
9035
7679
  } catch (err) {
9036
7680
  return {
@@ -9040,7 +7684,7 @@ async function cleanupLegacyDir(home) {
9040
7684
  }
9041
7685
  }
9042
7686
  async function cleanupLegacySymlink(home) {
9043
- const path4 = resolve18(home, LEGACY_SKILL_SYMLINK_RELATIVE);
7687
+ const path4 = resolve16(home, LEGACY_SKILL_SYMLINK_RELATIVE);
9044
7688
  try {
9045
7689
  let lst;
9046
7690
  try {
@@ -9062,7 +7706,7 @@ async function cleanupLegacySymlink(home) {
9062
7706
  }
9063
7707
  }
9064
7708
  async function cleanupLegacyMcp(home) {
9065
- const path4 = resolve18(home, CLAUDE_SETTINGS_JSON_RELATIVE);
7709
+ const path4 = resolve16(home, CLAUDE_SETTINGS_JSON_RELATIVE);
9066
7710
  try {
9067
7711
  const { data, existed } = await readJsonFile(
9068
7712
  path4,
@@ -9118,13 +7762,13 @@ function errorMessage(err) {
9118
7762
  }
9119
7763
 
9120
7764
  // src/migration/log.ts
9121
- import { appendFile as appendFile2, mkdir as mkdir11 } from "fs/promises";
7765
+ import { appendFile as appendFile2, mkdir as mkdir8 } from "fs/promises";
9122
7766
  import { homedir as homedir14 } from "os";
9123
- import { dirname as dirname10, resolve as resolve19 } from "path";
7767
+ import { dirname as dirname8, resolve as resolve17 } from "path";
9124
7768
  var MIGRATION_LOG_RELATIVE_PATH = ".rush/plugins/migration-failed.log";
9125
7769
  function resolveMigrationLogPath(opts = {}) {
9126
7770
  const home = opts.home ?? homedir14();
9127
- return resolve19(home, MIGRATION_LOG_RELATIVE_PATH);
7771
+ return resolve17(home, MIGRATION_LOG_RELATIVE_PATH);
9128
7772
  }
9129
7773
  async function appendMigrationFailure(reason, opts = {}) {
9130
7774
  const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
@@ -9141,31 +7785,31 @@ async function appendMigrationFailure(reason, opts = {}) {
9141
7785
  const payload = `${lines.join("\n")}
9142
7786
  `;
9143
7787
  try {
9144
- await mkdir11(dirname10(logPath), { recursive: true });
7788
+ await mkdir8(dirname8(logPath), { recursive: true });
9145
7789
  await appendFile2(logPath, payload, { encoding: "utf8" });
9146
7790
  } catch {
9147
7791
  }
9148
7792
  }
9149
7793
 
9150
7794
  // src/migration/migrate.ts
9151
- import { access as access14, readFile as readFile15 } from "fs/promises";
7795
+ import { access as access11, readFile as readFile12 } from "fs/promises";
9152
7796
  import { homedir as homedir16 } from "os";
9153
7797
 
9154
7798
  // src/plugins/resolver.ts
9155
- import { randomUUID as randomUUID9 } from "crypto";
9156
- import { mkdir as mkdir12, rename as rename9, rm as rm13 } from "fs/promises";
7799
+ import { randomUUID as randomUUID6 } from "crypto";
7800
+ import { mkdir as mkdir9, rename as rename6, rm as rm10 } from "fs/promises";
9157
7801
  import { homedir as homedir15 } from "os";
9158
7802
  import {
9159
- isAbsolute as isAbsolute5,
7803
+ isAbsolute as isAbsolute4,
9160
7804
  relative as pathRelative2,
9161
- resolve as pathResolve3,
7805
+ resolve as pathResolve,
9162
7806
  sep as pathSep
9163
7807
  } from "path";
9164
7808
 
9165
7809
  // src/plugins/capabilities.ts
9166
- import { constants as fsConstants11 } from "fs";
9167
- import { access as access12, readdir as readdir5, stat as stat11 } from "fs/promises";
9168
- import { extname as extname2, resolve as resolve20 } from "path";
7810
+ import { constants as fsConstants8 } from "fs";
7811
+ import { access as access9, readdir as readdir4, stat as stat7 } from "fs/promises";
7812
+ import { extname as extname2, resolve as resolve18 } from "path";
9169
7813
  var CAPABILITY_ORDER = [
9170
7814
  "commands",
9171
7815
  "skills",
@@ -9194,20 +7838,20 @@ async function scanCapabilities(sourceDir, manifest) {
9194
7838
  return CAPABILITY_ORDER.filter((cap) => found.has(cap));
9195
7839
  }
9196
7840
  async function hasCommands(sourceDir) {
9197
- return dirHasFileWithExt(resolve20(sourceDir, "commands"), ".md");
7841
+ return dirHasFileWithExt(resolve18(sourceDir, "commands"), ".md");
9198
7842
  }
9199
7843
  async function hasSkills(sourceDir) {
9200
- const skillsDir = resolve20(sourceDir, "skills");
7844
+ const skillsDir = resolve18(sourceDir, "skills");
9201
7845
  if (await dirExists3(skillsDir)) {
9202
7846
  let entries;
9203
7847
  try {
9204
- entries = await readdir5(skillsDir, { withFileTypes: true });
7848
+ entries = await readdir4(skillsDir, { withFileTypes: true });
9205
7849
  } catch {
9206
7850
  return false;
9207
7851
  }
9208
7852
  for (const entry of entries) {
9209
7853
  if (!entry.isDirectory()) continue;
9210
- const skillMdPath = resolve20(skillsDir, entry.name, "SKILL.md");
7854
+ const skillMdPath = resolve18(skillsDir, entry.name, "SKILL.md");
9211
7855
  if (await fileExists2(skillMdPath)) {
9212
7856
  return true;
9213
7857
  }
@@ -9216,16 +7860,16 @@ async function hasSkills(sourceDir) {
9216
7860
  return hasClaudeStyleSkills(sourceDir);
9217
7861
  }
9218
7862
  async function hasRules(sourceDir) {
9219
- return dirHasFileWithExt(resolve20(sourceDir, "rules"), ".md");
7863
+ return dirHasFileWithExt(resolve18(sourceDir, "rules"), ".md");
9220
7864
  }
9221
7865
  async function hasHooks(sourceDir) {
9222
- const hooksDir = resolve20(sourceDir, "hooks");
7866
+ const hooksDir = resolve18(sourceDir, "hooks");
9223
7867
  if (!await dirExists3(hooksDir)) {
9224
7868
  return false;
9225
7869
  }
9226
7870
  let entries;
9227
7871
  try {
9228
- entries = await readdir5(hooksDir, { withFileTypes: true });
7872
+ entries = await readdir4(hooksDir, { withFileTypes: true });
9229
7873
  } catch {
9230
7874
  return false;
9231
7875
  }
@@ -9251,7 +7895,7 @@ async function dirHasFileWithExt(dirPath, ext) {
9251
7895
  }
9252
7896
  let entries;
9253
7897
  try {
9254
- entries = await readdir5(dirPath, { withFileTypes: true });
7898
+ entries = await readdir4(dirPath, { withFileTypes: true });
9255
7899
  } catch {
9256
7900
  return false;
9257
7901
  }
@@ -9261,19 +7905,19 @@ async function dirHasFileWithExt(dirPath, ext) {
9261
7905
  );
9262
7906
  }
9263
7907
  async function hasClaudeStyleSkills(sourceDir) {
9264
- const dotSkillsDir = resolve20(sourceDir, ".skills");
7908
+ const dotSkillsDir = resolve18(sourceDir, ".skills");
9265
7909
  if (!await dirExists3(dotSkillsDir)) {
9266
7910
  return false;
9267
7911
  }
9268
7912
  async function walk2(dirPath) {
9269
7913
  let entries;
9270
7914
  try {
9271
- entries = await readdir5(dirPath, { withFileTypes: true });
7915
+ entries = await readdir4(dirPath, { withFileTypes: true });
9272
7916
  } catch {
9273
7917
  return false;
9274
7918
  }
9275
7919
  for (const entry of entries) {
9276
- const abs = resolve20(dirPath, entry.name);
7920
+ const abs = resolve18(dirPath, entry.name);
9277
7921
  if (entry.isDirectory()) {
9278
7922
  if (await walk2(abs)) return true;
9279
7923
  continue;
@@ -9288,7 +7932,7 @@ async function hasClaudeStyleSkills(sourceDir) {
9288
7932
  }
9289
7933
  async function dirExists3(p) {
9290
7934
  try {
9291
- const s = await stat11(p);
7935
+ const s = await stat7(p);
9292
7936
  return s.isDirectory();
9293
7937
  } catch {
9294
7938
  return false;
@@ -9296,8 +7940,8 @@ async function dirExists3(p) {
9296
7940
  }
9297
7941
  async function fileExists2(p) {
9298
7942
  try {
9299
- await access12(p, fsConstants11.R_OK);
9300
- const s = await stat11(p);
7943
+ await access9(p, fsConstants8.R_OK);
7944
+ const s = await stat7(p);
9301
7945
  return s.isFile();
9302
7946
  } catch {
9303
7947
  return false;
@@ -9366,18 +8010,18 @@ var PluginCloneFailedError = class extends PluginResolverError {
9366
8010
  };
9367
8011
 
9368
8012
  // src/plugins/manifest.ts
9369
- import { constants as fsConstants12 } from "fs";
9370
- import { access as access13, readFile as readFile14 } from "fs/promises";
9371
- import { resolve as resolve21 } from "path";
8013
+ import { constants as fsConstants9 } from "fs";
8014
+ import { access as access10, readFile as readFile11 } from "fs/promises";
8015
+ import { resolve as resolve19 } from "path";
9372
8016
  var PLUGIN_MANIFEST_RELATIVE_PATH = ".claude-plugin/plugin.json";
9373
8017
  async function readPluginManifest(pluginName, sourceDir) {
9374
- const manifestPath = resolve21(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
8018
+ const manifestPath = resolve19(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
9375
8019
  try {
9376
- await access13(manifestPath, fsConstants12.R_OK);
8020
+ await access10(manifestPath, fsConstants9.R_OK);
9377
8021
  } catch {
9378
8022
  throw new PluginManifestNotFoundError(pluginName, manifestPath);
9379
8023
  }
9380
- const raw = await readFile14(manifestPath, "utf8");
8024
+ const raw = await readFile11(manifestPath, "utf8");
9381
8025
  return parsePluginManifest(pluginName, raw, manifestPath);
9382
8026
  }
9383
8027
  function parsePluginManifest(pluginName, raw, sourcePathForError) {
@@ -9432,10 +8076,17 @@ async function resolvePlugin(ref, marketplace, options = {}) {
9432
8076
  });
9433
8077
  const sourceDir = resolvePluginSourceDir(ref, marketplace, entry);
9434
8078
  const manifest = await readPluginManifest(ref.name, sourceDir);
9435
- const capabilities = await scanCapabilities(sourceDir, manifest);
8079
+ const manifestWithImplicitMcp = await injectImplicitMcpRef(
8080
+ sourceDir,
8081
+ manifest
8082
+ );
8083
+ const capabilities = await scanCapabilities(
8084
+ sourceDir,
8085
+ manifestWithImplicitMcp
8086
+ );
9436
8087
  const normalizedManifest = normalizeRushMcpCommand(
9437
8088
  ref,
9438
- manifest,
8089
+ manifestWithImplicitMcp,
9439
8090
  options.rushAiBinaryResolver
9440
8091
  );
9441
8092
  return {
@@ -9463,17 +8114,17 @@ function resolvePluginSourceDir(ref, marketplace, entry) {
9463
8114
  return extractUrlSourceDir(marketplace, entry);
9464
8115
  }
9465
8116
  const relPath = extractRelativeSourcePath(ref, marketplace, entry);
9466
- if (isAbsolute5(relPath)) {
8117
+ if (isAbsolute4(relPath)) {
9467
8118
  throw new PluginSourceUnresolvableError(
9468
8119
  ref.name,
9469
8120
  marketplace.name,
9470
8121
  `source path must be relative to marketplace root; got absolute '${relPath}'`
9471
8122
  );
9472
8123
  }
9473
- const rootAbs = pathResolve3(marketplace.rootDir);
9474
- const abs = pathResolve3(rootAbs, relPath);
8124
+ const rootAbs = pathResolve(marketplace.rootDir);
8125
+ const abs = pathResolve(rootAbs, relPath);
9475
8126
  const rel = pathRelative2(rootAbs, abs);
9476
- const isEscaping = rel === ".." || rel.startsWith(`..${pathSep}`) || isAbsolute5(rel);
8127
+ const isEscaping = rel === ".." || rel.startsWith(`..${pathSep}`) || isAbsolute4(rel);
9477
8128
  if (isEscaping) {
9478
8129
  throw new PluginSourceUnresolvableError(
9479
8130
  ref.name,
@@ -9488,16 +8139,16 @@ function extractUrlSourceDir(marketplace, entry) {
9488
8139
  const cacheDir = pluginCacheDir(marketplace.name, entry.name);
9489
8140
  const subPath = typeof src.path === "string" ? src.path.trim() : "";
9490
8141
  if (!subPath) return cacheDir;
9491
- if (isAbsolute5(subPath)) {
8142
+ if (isAbsolute4(subPath)) {
9492
8143
  throw new PluginSourceUnresolvableError(
9493
8144
  entry.name,
9494
8145
  marketplace.name,
9495
8146
  `URL source subpath must be relative; got absolute '${subPath}'`
9496
8147
  );
9497
8148
  }
9498
- const resolved = pathResolve3(cacheDir, subPath);
8149
+ const resolved = pathResolve(cacheDir, subPath);
9499
8150
  const rel = pathRelative2(cacheDir, resolved);
9500
- if (rel === ".." || rel.startsWith(`..${pathSep}`) || isAbsolute5(rel)) {
8151
+ if (rel === ".." || rel.startsWith(`..${pathSep}`) || isAbsolute4(rel)) {
9501
8152
  throw new PluginSourceUnresolvableError(
9502
8153
  entry.name,
9503
8154
  marketplace.name,
@@ -9539,6 +8190,12 @@ function extractRelativeSourcePath(ref, marketplace, entry) {
9539
8190
  `source field has unexpected type '${typeof entry.source}'`
9540
8191
  );
9541
8192
  }
8193
+ async function injectImplicitMcpRef(sourceDir, manifest) {
8194
+ if (manifest.mcpServers !== void 0) return manifest;
8195
+ const mcpJsonPath = pathResolve(sourceDir, ".mcp.json");
8196
+ if (!await pathExists3(mcpJsonPath)) return manifest;
8197
+ return { ...manifest, mcpServers: "./.mcp.json" };
8198
+ }
9542
8199
  function normalizeRushMcpCommand(ref, manifest, rushAiBinaryResolver) {
9543
8200
  if (ref.name !== RUSH_AI_PLUGIN_NAME3 || ref.marketplace !== RUSH_AI_MARKETPLACE_NAME3 && ref.marketplace !== RUSH_AI_MARKETPLACE_NAME_NEW2) {
9544
8201
  return manifest;
@@ -9555,11 +8212,11 @@ function normalizeRushMcpCommand(ref, manifest, rushAiBinaryResolver) {
9555
8212
  if (typeof currentCommand !== "string" || currentCommand.length === 0) {
9556
8213
  return manifest;
9557
8214
  }
9558
- if (isAbsolute5(currentCommand)) {
8215
+ if (isAbsolute4(currentCommand)) {
9559
8216
  return manifest;
9560
8217
  }
9561
8218
  const resolved = (rushAiBinaryResolver ?? defaultRushAiBinaryResolver2)();
9562
- if (!resolved || !isAbsolute5(resolved)) {
8219
+ if (!resolved || !isAbsolute4(resolved)) {
9563
8220
  return manifest;
9564
8221
  }
9565
8222
  const nextServers = {
@@ -9570,7 +8227,7 @@ function normalizeRushMcpCommand(ref, manifest, rushAiBinaryResolver) {
9570
8227
  }
9571
8228
  function defaultRushAiBinaryResolver2() {
9572
8229
  const argv1 = process.argv[1];
9573
- if (typeof argv1 === "string" && isAbsolute5(argv1)) {
8230
+ if (typeof argv1 === "string" && isAbsolute4(argv1)) {
9574
8231
  return argv1;
9575
8232
  }
9576
8233
  return void 0;
@@ -9578,7 +8235,7 @@ function defaultRushAiBinaryResolver2() {
9578
8235
  function pluginCacheDir(marketplaceName, pluginName) {
9579
8236
  assertSafePathComponent(marketplaceName, "marketplace name");
9580
8237
  assertSafePathComponent(pluginName, "plugin name");
9581
- return pathResolve3(
8238
+ return pathResolve(
9582
8239
  homedir15(),
9583
8240
  ".rush",
9584
8241
  "plugin-cache",
@@ -9604,7 +8261,7 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
9604
8261
  if (!src || typeof src !== "object" || Array.isArray(src)) return;
9605
8262
  if (typeof src.url !== "string" || !src.url) return;
9606
8263
  const cacheDir = pluginCacheDir(marketplace.name, entry.name);
9607
- if (await pathExists5(cacheDir)) return;
8264
+ if (await pathExists3(cacheDir)) return;
9608
8265
  if (opts?.dryRun) {
9609
8266
  throw new PluginSourceUnresolvableError(
9610
8267
  ref.name,
@@ -9613,9 +8270,9 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
9613
8270
  );
9614
8271
  }
9615
8272
  const git = opts?.runner ?? defaultGitRunner;
9616
- const parent = pathResolve3(cacheDir, "..");
9617
- await mkdir12(parent, { recursive: true });
9618
- const tmpDir = `${cacheDir}.${randomUUID9()}.tmp`;
8273
+ const parent = pathResolve(cacheDir, "..");
8274
+ await mkdir9(parent, { recursive: true });
8275
+ const tmpDir = `${cacheDir}.${randomUUID6()}.tmp`;
9619
8276
  try {
9620
8277
  const args = ["clone", "--depth", "1"];
9621
8278
  if (typeof src.ref === "string" && src.ref.length > 0) {
@@ -9641,16 +8298,16 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
9641
8298
  }
9642
8299
  }
9643
8300
  try {
9644
- await rename9(tmpDir, cacheDir);
8301
+ await rename6(tmpDir, cacheDir);
9645
8302
  } catch (err) {
9646
- if (err.code === "ENOTEMPTY" && await pathExists5(cacheDir)) {
9647
- await rm13(tmpDir, { recursive: true, force: true });
8303
+ if (err.code === "ENOTEMPTY" && await pathExists3(cacheDir)) {
8304
+ await rm10(tmpDir, { recursive: true, force: true });
9648
8305
  return;
9649
8306
  }
9650
8307
  throw err;
9651
8308
  }
9652
8309
  } catch (err) {
9653
- await rm13(tmpDir, { recursive: true, force: true }).catch(() => {
8310
+ await rm10(tmpDir, { recursive: true, force: true }).catch(() => {
9654
8311
  });
9655
8312
  if (err instanceof PluginSourceUnresolvableError) throw err;
9656
8313
  throw new PluginCloneFailedError(ref.name, marketplace.name, err);
@@ -9880,7 +8537,7 @@ function isTruthyEnvValue(value) {
9880
8537
  }
9881
8538
  async function verifyNewFormat(manifestPath, expectedName, _expectedVersion) {
9882
8539
  try {
9883
- await access14(manifestPath);
8540
+ await access11(manifestPath);
9884
8541
  } catch (err) {
9885
8542
  return {
9886
8543
  ok: false,
@@ -9889,7 +8546,7 @@ async function verifyNewFormat(manifestPath, expectedName, _expectedVersion) {
9889
8546
  }
9890
8547
  let raw;
9891
8548
  try {
9892
- raw = await readFile15(manifestPath, "utf8");
8549
+ raw = await readFile12(manifestPath, "utf8");
9893
8550
  } catch (err) {
9894
8551
  return {
9895
8552
  ok: false,
@@ -10270,9 +8927,9 @@ async function runInstall(input) {
10270
8927
  }
10271
8928
  }
10272
8929
  if (marketplace.source.kind === "rush") {
10273
- const pluginDir = resolve22(marketplace.rootDir, "plugins", ref.name);
10274
- const manifestPath = resolve22(pluginDir, ".claude-plugin/plugin.json");
10275
- const alreadyMaterialized = await pathExists5(manifestPath);
8930
+ const pluginDir = resolve20(marketplace.rootDir, "plugins", ref.name);
8931
+ const manifestPath = resolve20(pluginDir, ".claude-plugin/plugin.json");
8932
+ const alreadyMaterialized = await pathExists3(manifestPath);
10276
8933
  const hasNewSecrets = input.secrets && Object.keys(input.secrets).length > 0;
10277
8934
  const shouldMaterialize = !alreadyMaterialized || force || hasNewSecrets;
10278
8935
  if (shouldMaterialize && dryRun && !alreadyMaterialized) {
@@ -10337,7 +8994,7 @@ async function runInstall(input) {
10337
8994
  if (force && !dryRun) {
10338
8995
  const entry = marketplace.manifest.plugins.find((p) => p.name === ref.name);
10339
8996
  if (entry && hasUrlSource(entry)) {
10340
- await rm14(pluginCacheDir(ref.marketplace, ref.name), {
8997
+ await rm11(pluginCacheDir(ref.marketplace, ref.name), {
10341
8998
  recursive: true,
10342
8999
  force: true
10343
9000
  });
@@ -10463,6 +9120,24 @@ async function runInstall(input) {
10463
9120
  registryError = formatRegistryError(err);
10464
9121
  }
10465
9122
  }
9123
+ let codexMirrorSyncError;
9124
+ const codexResult = results.find((r) => r.target === "codex");
9125
+ if (!dryRun && codexResult?.status === "ok" && !skipCodexMirrorSync(input)) {
9126
+ try {
9127
+ const [{ syncCodexMarketplaceMirror: syncCodexMarketplaceMirror2 }, { pickContentLoader: pickContentLoader2 }] = await Promise.all([
9128
+ import("./mirror-IHLOSPAS.js"),
9129
+ import("./_codex-content-loader-Q7KBWZSB.js")
9130
+ ]);
9131
+ await syncCodexMarketplaceMirror2(input.home ?? homedir17(), marketplace, {
9132
+ contentLoader: pickContentLoader2(marketplace)
9133
+ });
9134
+ } catch (err) {
9135
+ codexMirrorSyncError = err instanceof Error ? err.message : String(err);
9136
+ output.warn(
9137
+ `[codex] post-install marketplace mirror sync failed: ${codexMirrorSyncError}`
9138
+ );
9139
+ }
9140
+ }
10466
9141
  const hasFailed = results.some((r) => r.status === "failed");
10467
9142
  const exitCode = hasFailed ? 1 : 0;
10468
9143
  return {
@@ -10473,9 +9148,15 @@ async function runInstall(input) {
10473
9148
  dryRun,
10474
9149
  exitCode,
10475
9150
  ...registryError !== void 0 ? { registryError } : {},
9151
+ ...codexMirrorSyncError !== void 0 ? { codexMirrorSyncError } : {},
10476
9152
  ...migrationOutcome !== void 0 ? { migration: migrationOutcome } : {}
10477
9153
  };
10478
9154
  }
9155
+ function skipCodexMirrorSync(input) {
9156
+ if (input.skipCodexMirrorSync === true) return true;
9157
+ if (input.installerFactory !== void 0) return true;
9158
+ return false;
9159
+ }
10479
9160
  function formatRegistryError(err) {
10480
9161
  if (err instanceof RushRegistryConflictError) {
10481
9162
  return `rush-ai registry write conflict: ${err.message}`;
@@ -11258,7 +9939,7 @@ function getReskillInvocation() {
11258
9939
  };
11259
9940
  }
11260
9941
  function runReskill(args) {
11261
- return new Promise((resolve25, reject) => {
9942
+ return new Promise((resolve23, reject) => {
11262
9943
  const reskill = getReskillInvocation();
11263
9944
  const child = spawn2(reskill.command, [...reskill.args, ...args], {
11264
9945
  env: createReskillEnv(),
@@ -11268,10 +9949,10 @@ function runReskill(args) {
11268
9949
  child.on("close", (code, signal) => {
11269
9950
  if (signal) {
11270
9951
  output.error(`reskill exited from signal ${signal}`);
11271
- resolve25(1);
9952
+ resolve23(1);
11272
9953
  return;
11273
9954
  }
11274
- resolve25(code ?? 1);
9955
+ resolve23(code ?? 1);
11275
9956
  });
11276
9957
  });
11277
9958
  }
@@ -11345,7 +10026,7 @@ function registerSkillCommand(program) {
11345
10026
 
11346
10027
  // src/commands/task/index.ts
11347
10028
  import { createWriteStream } from "fs";
11348
- import { mkdir as mkdir13, readFile as readFile16, stat as stat12 } from "fs/promises";
10029
+ import { mkdir as mkdir10, readFile as readFile13, stat as stat8 } from "fs/promises";
11349
10030
  import path3 from "path";
11350
10031
  import { Readable } from "stream";
11351
10032
  import { pipeline } from "stream/promises";
@@ -11371,13 +10052,13 @@ async function readStdinIfPiped() {
11371
10052
  if (process.stdin.isTTY) {
11372
10053
  return null;
11373
10054
  }
11374
- return new Promise((resolve25, reject) => {
10055
+ return new Promise((resolve23, reject) => {
11375
10056
  const chunks = [];
11376
10057
  process.stdin.on("data", (chunk) => {
11377
10058
  chunks.push(chunk);
11378
10059
  });
11379
10060
  process.stdin.on("end", () => {
11380
- resolve25(Buffer.concat(chunks).toString("utf-8").trim());
10061
+ resolve23(Buffer.concat(chunks).toString("utf-8").trim());
11381
10062
  });
11382
10063
  process.stdin.on("error", reject);
11383
10064
  });
@@ -11578,7 +10259,7 @@ function buildDeployUrl(prefix, env, apiBaseUrl) {
11578
10259
  }
11579
10260
 
11580
10261
  // src/commands/task/push.ts
11581
- import { resolve as resolve23 } from "path";
10262
+ import { resolve as resolve21 } from "path";
11582
10263
  import chalk5 from "chalk";
11583
10264
 
11584
10265
  // src/util/env-file.ts
@@ -11722,7 +10403,7 @@ function registerPushSubcommand(task, program) {
11722
10403
  requireAuth();
11723
10404
  const format = resolveFormat(program.opts());
11724
10405
  const client = createClient();
11725
- const projectPath = resolve23(opts.path);
10406
+ const projectPath = resolve21(opts.path);
11726
10407
  if (format !== "json") {
11727
10408
  output.info("Checking project readiness...");
11728
10409
  }
@@ -12153,7 +10834,7 @@ async function confirmPublish(args) {
12153
10834
  return answer.trim().toLowerCase().startsWith("y");
12154
10835
  }
12155
10836
  function readOneLine() {
12156
- return new Promise((resolve25) => {
10837
+ return new Promise((resolve23) => {
12157
10838
  let acc = "";
12158
10839
  const onData = (chunk) => {
12159
10840
  acc += chunk.toString("utf-8");
@@ -12161,7 +10842,7 @@ function readOneLine() {
12161
10842
  if (nlIdx !== -1) {
12162
10843
  process.stdin.removeListener("data", onData);
12163
10844
  process.stdin.pause();
12164
- resolve25(acc.slice(0, nlIdx));
10845
+ resolve23(acc.slice(0, nlIdx));
12165
10846
  }
12166
10847
  };
12167
10848
  process.stdin.resume();
@@ -12347,12 +11028,12 @@ function registerDomainSubcommand(task, program) {
12347
11028
  }
12348
11029
 
12349
11030
  // src/commands/task/env.ts
12350
- import { resolve as resolve24 } from "path";
11031
+ import { resolve as resolve22 } from "path";
12351
11032
 
12352
11033
  // src/util/prompt.ts
12353
11034
  import { createInterface } from "readline";
12354
11035
  function promptSecret(message) {
12355
- return new Promise((resolve25, reject) => {
11036
+ return new Promise((resolve23, reject) => {
12356
11037
  const rl = createInterface({
12357
11038
  input: process.stdin,
12358
11039
  output: process.stdout,
@@ -12365,7 +11046,7 @@ function promptSecret(message) {
12365
11046
  rl.question("", (answer) => {
12366
11047
  rl.close();
12367
11048
  process.stdout.write("\n");
12368
- resolve25(answer);
11049
+ resolve23(answer);
12369
11050
  });
12370
11051
  rl.on("SIGINT", () => {
12371
11052
  rl.close();
@@ -12388,7 +11069,7 @@ var NO_PROJECT_HINT2 = [
12388
11069
  ].join("\n");
12389
11070
  function resolveProjectId(opts) {
12390
11071
  if (opts.project) return Promise.resolve(opts.project);
12391
- const projectPath = resolve24(opts.path ?? ".");
11072
+ const projectPath = resolve22(opts.path ?? ".");
12392
11073
  const remoteUrl = isGitRepo(projectPath) ? getRemoteUrl(projectPath) : null;
12393
11074
  const idFromRemote = remoteUrl ? extractRushProjectId(remoteUrl) : null;
12394
11075
  if (idFromRemote) return Promise.resolve(idFromRemote);
@@ -13116,7 +11797,7 @@ async function downloadFile(file2, destPath, client) {
13116
11797
  if (!response.body) {
13117
11798
  throw new Error("No response body");
13118
11799
  }
13119
- await mkdir13(path3.dirname(destPath), { recursive: true });
11800
+ await mkdir10(path3.dirname(destPath), { recursive: true });
13120
11801
  const nodeStream = Readable.fromWeb(
13121
11802
  response.body
13122
11803
  );
@@ -13657,7 +12338,7 @@ async function uploadFileForTask(client, localPath) {
13657
12338
  const resolved = path3.resolve(localPath);
13658
12339
  let fileStat;
13659
12340
  try {
13660
- fileStat = await stat12(resolved);
12341
+ fileStat = await stat8(resolved);
13661
12342
  } catch {
13662
12343
  throw new RushError(`File not found: ${localPath}`);
13663
12344
  }
@@ -13671,7 +12352,7 @@ async function uploadFileForTask(client, localPath) {
13671
12352
  }
13672
12353
  const filename = path3.basename(resolved);
13673
12354
  const mediaType = guessMediaType(resolved);
13674
- const buf = await readFile16(resolved);
12355
+ const buf = await readFile13(resolved);
13675
12356
  const blob = new File([buf], filename, { type: mediaType });
13676
12357
  const formData = new FormData();
13677
12358
  formData.append("file", blob);
@@ -13724,9 +12405,9 @@ function registerCommands(program) {
13724
12405
  }
13725
12406
 
13726
12407
  // src/util/update-check.ts
13727
- import { mkdir as mkdir14, readFile as readFile17, writeFile as writeFile10 } from "fs/promises";
13728
- import { homedir as homedir17 } from "os";
13729
- import { dirname as dirname11, join as join10 } from "path";
12408
+ import { mkdir as mkdir11, readFile as readFile14, writeFile as writeFile7 } from "fs/promises";
12409
+ import { homedir as homedir18 } from "os";
12410
+ import { dirname as dirname9, join as join10 } from "path";
13730
12411
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
13731
12412
  var FETCH_TIMEOUT_MS = 3e3;
13732
12413
  var REGISTRY_URL = "https://registry.npmjs.org/rush-ai/latest";
@@ -13749,19 +12430,19 @@ function isNewerVersion(current, latest) {
13749
12430
  }
13750
12431
  async function writeLastCheck(checkFile) {
13751
12432
  try {
13752
- await mkdir14(dirname11(checkFile), { recursive: true });
13753
- await writeFile10(checkFile, JSON.stringify({ lastCheck: Date.now() }));
12433
+ await mkdir11(dirname9(checkFile), { recursive: true });
12434
+ await writeFile7(checkFile, JSON.stringify({ lastCheck: Date.now() }));
13754
12435
  } catch {
13755
12436
  }
13756
12437
  }
13757
12438
  async function checkForUpdate(currentVersion) {
13758
- const rushDir = join10(homedir17(), ".rush");
12439
+ const rushDir = join10(homedir18(), ".rush");
13759
12440
  const checkFile = join10(rushDir, "update-check.json");
13760
12441
  try {
13761
12442
  if (process.env.CI) return;
13762
12443
  let lastCheck = 0;
13763
12444
  try {
13764
- const data = JSON.parse(await readFile17(checkFile, "utf-8"));
12445
+ const data = JSON.parse(await readFile14(checkFile, "utf-8"));
13765
12446
  lastCheck = data.lastCheck ?? 0;
13766
12447
  } catch {
13767
12448
  }