rush-ai 0.10.0 → 0.11.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,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ consumeSSEStreamWithReconnect
4
+ } from "./chunk-MG4HY2PD.js";
5
+ import {
6
+ AtomicJsonConflictError,
7
+ pathExists,
8
+ readJsonFile,
9
+ writeJsonFile
10
+ } from "./chunk-V7P2TZZ4.js";
2
11
  import {
3
12
  ApiError,
4
13
  AuthError,
@@ -7,7 +16,6 @@ import {
7
16
  VERSION,
8
17
  checkAuthSourceMismatch,
9
18
  clearAuthConfig,
10
- consumeSSEStreamWithReconnect,
11
19
  createClient,
12
20
  createProfile,
13
21
  deleteProfile,
@@ -29,7 +37,7 @@ import {
29
37
  setGlobalConfig,
30
38
  setVerbosity,
31
39
  verifyCurrentAuthSession
32
- } from "./chunk-SZO6LEQX.js";
40
+ } from "./chunk-3X5X3ZTX.js";
33
41
 
34
42
  // src/index.ts
35
43
  import chalk7 from "chalk";
@@ -2897,7 +2905,7 @@ var MarketplaceCache = class {
2897
2905
  */
2898
2906
  async remove(name) {
2899
2907
  const target = this.pathFor(name);
2900
- if (!await pathExists(target)) {
2908
+ if (!await pathExists2(target)) {
2901
2909
  return;
2902
2910
  }
2903
2911
  await rm2(target, { recursive: true, force: true });
@@ -2944,7 +2952,7 @@ var MarketplaceCache = class {
2944
2952
  * v1 暂不单列损坏项;若未来需要,可加 `options.includeCorrupt` 返回 warning。
2945
2953
  */
2946
2954
  async list() {
2947
- if (!await pathExists(this.cacheBaseDir)) {
2955
+ if (!await pathExists2(this.cacheBaseDir)) {
2948
2956
  return [];
2949
2957
  }
2950
2958
  const entries = await readdir(this.cacheBaseDir, { withFileTypes: true });
@@ -3009,7 +3017,7 @@ var MarketplaceCache = class {
3009
3017
  }
3010
3018
  async readMeta(name) {
3011
3019
  const metaPath = this.metaPathFor(name);
3012
- if (!await pathExists(metaPath)) {
3020
+ if (!await pathExists2(metaPath)) {
3013
3021
  return null;
3014
3022
  }
3015
3023
  const raw = await readFile3(metaPath, "utf8");
@@ -3052,7 +3060,7 @@ var MarketplaceCache = class {
3052
3060
  await atomicWriteJson(metaPath, payload);
3053
3061
  }
3054
3062
  };
3055
- async function pathExists(p) {
3063
+ async function pathExists2(p) {
3056
3064
  try {
3057
3065
  await access3(p, fsConstants3.F_OK);
3058
3066
  return true;
@@ -3268,7 +3276,7 @@ function assertRegistryShape(raw, filePath) {
3268
3276
  );
3269
3277
  }
3270
3278
  }
3271
- async function pathExists2(p) {
3279
+ async function pathExists3(p) {
3272
3280
  try {
3273
3281
  await access4(p, fsConstants4.F_OK);
3274
3282
  return true;
@@ -3289,7 +3297,7 @@ async function atomicWrite(filePath, content) {
3289
3297
  }
3290
3298
  }
3291
3299
  async function loadFromPath(filePath) {
3292
- if (!await pathExists2(filePath)) {
3300
+ if (!await pathExists3(filePath)) {
3293
3301
  return {
3294
3302
  data: { schemaVersion: REGISTRY_SCHEMA_VERSION, plugins: {} },
3295
3303
  mtimeMs: null
@@ -3396,7 +3404,7 @@ var RushRegistryStore = class _RushRegistryStore {
3396
3404
  async saveWithRetry(attempt) {
3397
3405
  this.assertWriteableSchema();
3398
3406
  if (this.loadedMtimeMs === null) {
3399
- if (await pathExists2(this.filePath)) {
3407
+ if (await pathExists3(this.filePath)) {
3400
3408
  if (attempt === 0) {
3401
3409
  await this.reloadAndMerge();
3402
3410
  return this.saveWithRetry(1);
@@ -3790,7 +3798,7 @@ function truncate3(str, max) {
3790
3798
  function registerMcpCommand(program) {
3791
3799
  const mcp = program.command("mcp").description("MCP server and platform MCP discovery");
3792
3800
  mcp.command("serve").description("Start the MCP stdio server").action(async () => {
3793
- const { startMcpServer } = await import("./server-KFDYSF4T.js");
3801
+ const { startMcpServer } = await import("./server-GQMH2YN4.js");
3794
3802
  await startMcpServer();
3795
3803
  });
3796
3804
  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(
@@ -3798,7 +3806,6 @@ function registerMcpCommand(program) {
3798
3806
  "Max results per page (default: 50, max: 100)",
3799
3807
  "50"
3800
3808
  ).option("-p, --page <number>", "Page number (default: 1)", "1").action(async (opts) => {
3801
- requireAuth();
3802
3809
  const format = resolveFormat(program.opts());
3803
3810
  const client = createClient();
3804
3811
  const params = new URLSearchParams();
@@ -3858,7 +3865,6 @@ function registerMcpCommand(program) {
3858
3865
  }
3859
3866
  });
3860
3867
  mcp.command("list-tools <server-id>").description("List tools provided by an MCP server").action(async (serverId) => {
3861
- requireAuth();
3862
3868
  const format = resolveFormat(program.opts());
3863
3869
  const client = createClient();
3864
3870
  let data;
@@ -3904,113 +3910,102 @@ function registerMcpCommand(program) {
3904
3910
  })
3905
3911
  );
3906
3912
  });
3913
+ mcp.command("install <mcp-id>").description(
3914
+ "Install an MCP server from Rush registry to Claude Desktop / Claude Code"
3915
+ ).option("-y, --yes", "Skip confirmation prompts (use defaults)").option(
3916
+ "--target <targets>",
3917
+ "Comma-separated targets: claude-desktop,claude-code (default: both)"
3918
+ ).option("--allow-unverified", "Allow installing unverified MCP servers").action(async (mcpId, opts) => {
3919
+ const format = resolveFormat(program.opts());
3920
+ const { runMcpInstall, printMcpInstallSummary } = await import("./install-7J3LP75O.js");
3921
+ try {
3922
+ const result = await runMcpInstall({
3923
+ mcpId,
3924
+ yes: opts.yes,
3925
+ targetRaw: opts.target,
3926
+ allowUnverified: opts.allowUnverified
3927
+ });
3928
+ const anyOk = result.targets.some((t) => t.status === "ok");
3929
+ const anyError = result.targets.some((t) => t.status === "error");
3930
+ if (anyError || !anyOk) process.exitCode = 1;
3931
+ if (format === "json") {
3932
+ output.log(JSON.stringify(result, null, 2));
3933
+ return;
3934
+ }
3935
+ printMcpInstallSummary(result);
3936
+ } catch (err) {
3937
+ if (format === "json") {
3938
+ output.log(
3939
+ JSON.stringify(
3940
+ {
3941
+ error: err instanceof Error ? err.message : String(err)
3942
+ },
3943
+ null,
3944
+ 2
3945
+ )
3946
+ );
3947
+ } else {
3948
+ output.error(err instanceof Error ? err.message : String(err));
3949
+ }
3950
+ process.exitCode = 1;
3951
+ }
3952
+ });
3953
+ mcp.command("uninstall <mcp-id>").description(
3954
+ "Remove an MCP server from Claude Desktop / Claude Code config"
3955
+ ).option(
3956
+ "--target <targets>",
3957
+ "Comma-separated targets: claude-desktop,claude-code (default: both)"
3958
+ ).action(async (mcpId, opts) => {
3959
+ const format = resolveFormat(program.opts());
3960
+ const { runMcpUninstall, printMcpUninstallSummary } = await import("./install-7J3LP75O.js");
3961
+ try {
3962
+ const result = await runMcpUninstall({
3963
+ mcpId,
3964
+ targetRaw: opts.target
3965
+ });
3966
+ const anyError = result.targets.some((t) => t.error);
3967
+ if (anyError) process.exitCode = 1;
3968
+ if (format === "json") {
3969
+ output.log(JSON.stringify(result, null, 2));
3970
+ return;
3971
+ }
3972
+ printMcpUninstallSummary(result);
3973
+ } catch (err) {
3974
+ if (format === "json") {
3975
+ output.log(
3976
+ JSON.stringify(
3977
+ {
3978
+ error: err instanceof Error ? err.message : String(err)
3979
+ },
3980
+ null,
3981
+ 2
3982
+ )
3983
+ );
3984
+ } else {
3985
+ output.error(err instanceof Error ? err.message : String(err));
3986
+ }
3987
+ process.exitCode = 1;
3988
+ }
3989
+ });
3907
3990
  }
3908
3991
 
3909
- // src/installers/claude-code/atomic-json.ts
3992
+ // src/installers/claude-code/installer.ts
3910
3993
  import { randomUUID as randomUUID3 } from "crypto";
3911
- import { constants as fsConstants5 } from "fs";
3912
3994
  import {
3913
- access as access5,
3995
+ copyFile,
3996
+ lstat,
3914
3997
  mkdir as mkdir4,
3998
+ readdir as readdir2,
3915
3999
  readFile as readFile5,
4000
+ readlink,
3916
4001
  rename as rename3,
3917
4002
  rm as rm4,
3918
4003
  stat as stat2,
3919
- writeFile as writeFile4
3920
- } from "fs/promises";
3921
- import { dirname as dirname4 } from "path";
3922
- var AtomicJsonError = class extends Error {
3923
- constructor(message, filePath) {
3924
- super(message);
3925
- this.filePath = filePath;
3926
- this.name = "AtomicJsonError";
3927
- }
3928
- };
3929
- var AtomicJsonCorruptError = class extends AtomicJsonError {
3930
- constructor(filePath, cause) {
3931
- super(
3932
- `${filePath} \u89E3\u6790\u5931\u8D25\uFF0C\u62D2\u7EDD\u8BFB\u53D6\u3002\u8BF7\u624B\u5DE5\u4FEE\u590D\u6216\u5907\u4EFD\u540E\u5220\u9664\u3002\u539F\u56E0\uFF1A${String(
3933
- cause?.message ?? cause
3934
- )}`,
3935
- filePath
3936
- );
3937
- this.cause = cause;
3938
- this.name = "AtomicJsonCorruptError";
3939
- }
3940
- };
3941
- async function readJsonFile(filePath, defaults) {
3942
- if (!await pathExists3(filePath)) {
3943
- return { data: defaults(), mtimeMs: null, existed: false };
3944
- }
3945
- const stats = await stat2(filePath);
3946
- const raw = await readFile5(filePath, "utf8");
3947
- let parsed;
3948
- try {
3949
- parsed = JSON.parse(raw);
3950
- } catch (err) {
3951
- throw new AtomicJsonCorruptError(filePath, err);
3952
- }
3953
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3954
- throw new AtomicJsonCorruptError(
3955
- filePath,
3956
- new Error("\u9876\u5C42\u5FC5\u987B\u662F JSON object\uFF0C\u4E0D\u5141\u8BB8\u6570\u7EC4\u6216\u57FA\u672C\u7C7B\u578B")
3957
- );
3958
- }
3959
- return {
3960
- data: parsed,
3961
- mtimeMs: stats.mtimeMs,
3962
- existed: true
3963
- };
3964
- }
3965
- async function writeJsonFile(filePath, data, options = {}) {
3966
- const { indent = 2, trailingNewline = true } = options;
3967
- await mkdir4(dirname4(filePath), { recursive: true });
3968
- const payload = JSON.stringify(data, null, indent) + (trailingNewline ? "\n" : "");
3969
- const tmp = `${filePath}.${randomUUID3()}.tmp`;
3970
- try {
3971
- await writeFile4(tmp, payload, { encoding: "utf8", flag: "w" });
3972
- await rename3(tmp, filePath);
3973
- } catch (err) {
3974
- await rm4(tmp, { force: true }).catch(() => {
3975
- });
3976
- throw err;
3977
- }
3978
- }
3979
- var AtomicJsonConflictError = class extends AtomicJsonError {
3980
- constructor(filePath) {
3981
- super(
3982
- `${filePath} \u5728 read-modify-write \u671F\u95F4\u88AB\u5176\u4ED6\u8FDB\u7A0B\u4FEE\u6539\u3002\u8BF7\u91CD\u8BD5\u3002`,
3983
- filePath
3984
- );
3985
- this.name = "AtomicJsonConflictError";
3986
- }
3987
- };
3988
- async function pathExists3(p) {
3989
- try {
3990
- await access5(p, fsConstants5.F_OK);
3991
- return true;
3992
- } catch {
3993
- return false;
3994
- }
3995
- }
3996
-
3997
- // src/installers/claude-code/installer.ts
3998
- import { randomUUID as randomUUID4 } from "crypto";
3999
- import {
4000
- copyFile,
4001
- lstat,
4002
- mkdir as mkdir5,
4003
- readdir as readdir2,
4004
- readFile as readFile6,
4005
- readlink,
4006
- rename as rename4,
4007
- rm as rm5,
4008
- stat as stat3,
4009
4004
  symlink,
4010
4005
  unlink
4011
4006
  } from "fs/promises";
4012
4007
  import { homedir as homedir5 } from "os";
4013
- import { dirname as dirname5, join as join9, resolve as resolve9 } from "path";
4008
+ import { dirname as dirname4, join as join9, resolve as resolve9 } from "path";
4014
4009
 
4015
4010
  // src/installers/claude-code/mcp.ts
4016
4011
  import { execFileSync as execFileSync4 } from "child_process";
@@ -4169,7 +4164,7 @@ var ClaudeCodeInstaller = class {
4169
4164
  return dirExists(this.paths.claudeDir);
4170
4165
  }
4171
4166
  async isInstalled(ref) {
4172
- if (!await pathExists3(this.paths.installedPluginsJson)) {
4167
+ if (!await pathExists(this.paths.installedPluginsJson)) {
4173
4168
  return false;
4174
4169
  }
4175
4170
  const { data } = await readJsonFile(
@@ -4180,7 +4175,7 @@ var ClaudeCodeInstaller = class {
4180
4175
  return Array.isArray(entries) && entries.length > 0;
4181
4176
  }
4182
4177
  async list() {
4183
- if (!await pathExists3(this.paths.installedPluginsJson)) {
4178
+ if (!await pathExists(this.paths.installedPluginsJson)) {
4184
4179
  return [];
4185
4180
  }
4186
4181
  const { data } = await readJsonFile(
@@ -4251,13 +4246,13 @@ var ClaudeCodeInstaller = class {
4251
4246
  }
4252
4247
  try {
4253
4248
  const writtenFiles = [];
4254
- await mkdir5(pluginVersionDir, { recursive: true });
4249
+ await mkdir4(pluginVersionDir, { recursive: true });
4255
4250
  writtenFiles.push(pluginVersionDir);
4256
4251
  for (const cap of CAPABILITY_DIRS) {
4257
4252
  const srcDir = resolve9(plugin.sourceDir, cap);
4258
4253
  if (!await dirExists(srcDir)) continue;
4259
4254
  const dstDir = this.paths.capabilityDir(ref, version, cap);
4260
- await mkdir5(dstDir, { recursive: true });
4255
+ await mkdir4(dstDir, { recursive: true });
4261
4256
  writtenFiles.push(dstDir);
4262
4257
  const copied = await copyDirTracked(srcDir, dstDir);
4263
4258
  writtenFiles.push(...copied);
@@ -4392,8 +4387,8 @@ var ClaudeCodeInstaller = class {
4392
4387
  await unlink(mktDir);
4393
4388
  files.push(mktDir);
4394
4389
  } else if (kind === "dir") {
4395
- if (await pathExists3(join9(mktDir, ".rush-marketplace-source.json"))) {
4396
- await rm5(mktDir, { recursive: true, force: true });
4390
+ if (await pathExists(join9(mktDir, ".rush-marketplace-source.json"))) {
4391
+ await rm4(mktDir, { recursive: true, force: true });
4397
4392
  files.push(mktDir);
4398
4393
  }
4399
4394
  }
@@ -4412,7 +4407,7 @@ var ClaudeCodeInstaller = class {
4412
4407
  * 会把自己算作"还有引用"。
4413
4408
  */
4414
4409
  async marketplaceStillReferenced(marketplace) {
4415
- if (!await pathExists3(this.paths.installedPluginsJson)) {
4410
+ if (!await pathExists(this.paths.installedPluginsJson)) {
4416
4411
  return false;
4417
4412
  }
4418
4413
  const { data } = await readJsonFile(
@@ -4427,7 +4422,7 @@ var ClaudeCodeInstaller = class {
4427
4422
  return false;
4428
4423
  }
4429
4424
  async removeKnownMarketplaceEntry(marketplace) {
4430
- if (!await pathExists3(this.paths.knownMarketplacesJson)) {
4425
+ if (!await pathExists(this.paths.knownMarketplacesJson)) {
4431
4426
  return false;
4432
4427
  }
4433
4428
  return retryOnConflict(async () => {
@@ -4464,11 +4459,11 @@ var ClaudeCodeInstaller = class {
4464
4459
  return { written: false, path: "" };
4465
4460
  }
4466
4461
  const mktDir = this.paths.marketplaceInstallDir(marketplaceName);
4467
- await mkdir5(this.paths.marketplacesRootDir, { recursive: true });
4462
+ await mkdir4(this.paths.marketplacesRootDir, { recursive: true });
4468
4463
  const kind = await inodeKind(mktDir);
4469
4464
  if (kind === "symlink") {
4470
4465
  const target = await readlink(mktDir).catch(() => null);
4471
- const resolvedTarget = target !== null ? resolve9(dirname5(mktDir), target) : null;
4466
+ const resolvedTarget = target !== null ? resolve9(dirname4(mktDir), target) : null;
4472
4467
  if (resolvedTarget && resolvedTarget === resolve9(src.rootDir)) {
4473
4468
  return { written: false, path: mktDir };
4474
4469
  }
@@ -4478,7 +4473,7 @@ var ClaudeCodeInstaller = class {
4478
4473
  }
4479
4474
  if (kind === "dir") {
4480
4475
  const metaPath = join9(mktDir, ".rush-marketplace-source.json");
4481
- if (await pathExists3(metaPath)) {
4476
+ if (await pathExists(metaPath)) {
4482
4477
  const metaSourceRaw = await readMarketplaceCacheSource(metaPath);
4483
4478
  const currentSourceRaw = descriptorToSourceRaw(src.descriptor);
4484
4479
  if (metaSourceRaw !== null && metaSourceRaw === currentSourceRaw) {
@@ -4502,7 +4497,7 @@ var ClaudeCodeInstaller = class {
4502
4497
  return { written: true, path: mktDir };
4503
4498
  }
4504
4499
  await copyDirTracked(src.rootDir, mktDir).catch(async (err) => {
4505
- await rm5(mktDir, { recursive: true, force: true }).catch(() => {
4500
+ await rm4(mktDir, { recursive: true, force: true }).catch(() => {
4506
4501
  });
4507
4502
  throw err;
4508
4503
  });
@@ -4522,7 +4517,7 @@ var ClaudeCodeInstaller = class {
4522
4517
  return [];
4523
4518
  }
4524
4519
  async isAlreadyInstalledAtVersion(ref, version) {
4525
- if (!await pathExists3(this.paths.installedPluginsJson)) {
4520
+ if (!await pathExists(this.paths.installedPluginsJson)) {
4526
4521
  return false;
4527
4522
  }
4528
4523
  const { data } = await readJsonFile(
@@ -4576,7 +4571,7 @@ var ClaudeCodeInstaller = class {
4576
4571
  };
4577
4572
  }
4578
4573
  async isKnownMarketplaceRegistered(marketplace) {
4579
- if (!await pathExists3(this.paths.knownMarketplacesJson)) {
4574
+ if (!await pathExists(this.paths.knownMarketplacesJson)) {
4580
4575
  return false;
4581
4576
  }
4582
4577
  const { data } = await readJsonFile(
@@ -4586,7 +4581,7 @@ var ClaudeCodeInstaller = class {
4586
4581
  return Object.hasOwn(data, marketplace);
4587
4582
  }
4588
4583
  async wouldInstalledPluginsChange(ref, version, installPath) {
4589
- if (!await pathExists3(this.paths.installedPluginsJson)) {
4584
+ if (!await pathExists(this.paths.installedPluginsJson)) {
4590
4585
  return true;
4591
4586
  }
4592
4587
  const { data } = await readJsonFile(
@@ -4600,7 +4595,7 @@ var ClaudeCodeInstaller = class {
4600
4595
  return userEntry.version !== version || userEntry.installPath !== installPath;
4601
4596
  }
4602
4597
  async wouldSettingsChange(key) {
4603
- if (!await pathExists3(this.paths.settingsJson)) {
4598
+ if (!await pathExists(this.paths.settingsJson)) {
4604
4599
  return true;
4605
4600
  }
4606
4601
  const { data } = await readJsonFile(
@@ -4658,8 +4653,8 @@ var ClaudeCodeInstaller = class {
4658
4653
  const settings = await this.readRawJsonForRollback(this.paths.settingsJson);
4659
4654
  let backupVersionDir = null;
4660
4655
  if (preexistedVersion === "dir") {
4661
- backupVersionDir = `${pluginVersionDir}.rollback-${randomUUID4()}`;
4662
- await rename4(pluginVersionDir, backupVersionDir);
4656
+ backupVersionDir = `${pluginVersionDir}.rollback-${randomUUID3()}`;
4657
+ await rename3(pluginVersionDir, backupVersionDir);
4663
4658
  }
4664
4659
  return {
4665
4660
  ref,
@@ -4680,7 +4675,7 @@ var ClaudeCodeInstaller = class {
4680
4675
  */
4681
4676
  async finalizeRollback(snap) {
4682
4677
  if (snap.backupVersionDir) {
4683
- await rm5(snap.backupVersionDir, { recursive: true, force: true });
4678
+ await rm4(snap.backupVersionDir, { recursive: true, force: true });
4684
4679
  }
4685
4680
  }
4686
4681
  /**
@@ -4693,19 +4688,19 @@ var ClaudeCodeInstaller = class {
4693
4688
  */
4694
4689
  async applyRollback(snap) {
4695
4690
  if (snap.preexistedVersion === "dir" && snap.backupVersionDir) {
4696
- await rm5(snap.pluginVersionDir, { recursive: true, force: true });
4697
- await rename4(snap.backupVersionDir, snap.pluginVersionDir);
4691
+ await rm4(snap.pluginVersionDir, { recursive: true, force: true });
4692
+ await rename3(snap.backupVersionDir, snap.pluginVersionDir);
4698
4693
  } else if (snap.preexistedVersion === "missing") {
4699
- await rm5(snap.pluginVersionDir, { recursive: true, force: true });
4694
+ await rm4(snap.pluginVersionDir, { recursive: true, force: true });
4700
4695
  }
4701
4696
  if (snap.preexistedPlugin === "missing") {
4702
- await rm5(this.paths.pluginCacheDir(snap.ref), {
4697
+ await rm4(this.paths.pluginCacheDir(snap.ref), {
4703
4698
  recursive: true,
4704
4699
  force: true
4705
4700
  });
4706
4701
  }
4707
4702
  if (snap.preexistedMarketplace === "missing") {
4708
- await rm5(this.paths.marketplaceCacheDir(snap.ref.marketplace), {
4703
+ await rm4(this.paths.marketplaceCacheDir(snap.ref.marketplace), {
4709
4704
  recursive: true,
4710
4705
  force: true
4711
4706
  });
@@ -4719,7 +4714,7 @@ var ClaudeCodeInstaller = class {
4719
4714
  await unlink(mktInstallDir).catch(() => {
4720
4715
  });
4721
4716
  } else if (currentKind !== "missing") {
4722
- await rm5(mktInstallDir, { recursive: true, force: true }).catch(
4717
+ await rm4(mktInstallDir, { recursive: true, force: true }).catch(
4723
4718
  () => {
4724
4719
  }
4725
4720
  );
@@ -4730,7 +4725,7 @@ var ClaudeCodeInstaller = class {
4730
4725
  await this.restoreRawJson(this.paths.settingsJson, snap.settings);
4731
4726
  }
4732
4727
  async readRawJsonForRollback(path4) {
4733
- if (!await pathExists3(path4)) {
4728
+ if (!await pathExists(path4)) {
4734
4729
  return { existed: false };
4735
4730
  }
4736
4731
  const { data } = await readJsonFile(
@@ -4741,8 +4736,8 @@ var ClaudeCodeInstaller = class {
4741
4736
  }
4742
4737
  async restoreRawJson(path4, state) {
4743
4738
  if (!state.existed) {
4744
- if (await pathExists3(path4)) {
4745
- await rm5(path4, { force: true });
4739
+ if (await pathExists(path4)) {
4740
+ await rm4(path4, { force: true });
4746
4741
  }
4747
4742
  return;
4748
4743
  }
@@ -4903,7 +4898,7 @@ var ClaudeCodeInstaller = class {
4903
4898
  });
4904
4899
  }
4905
4900
  async removeInstalledPlugin(ref) {
4906
- if (!await pathExists3(this.paths.installedPluginsJson)) {
4901
+ if (!await pathExists(this.paths.installedPluginsJson)) {
4907
4902
  return false;
4908
4903
  }
4909
4904
  return retryOnConflict(async () => {
@@ -4933,7 +4928,7 @@ var ClaudeCodeInstaller = class {
4933
4928
  });
4934
4929
  }
4935
4930
  async disablePlugin(key) {
4936
- if (!await pathExists3(this.paths.settingsJson)) {
4931
+ if (!await pathExists(this.paths.settingsJson)) {
4937
4932
  return false;
4938
4933
  }
4939
4934
  return retryOnConflict(async () => {
@@ -4959,7 +4954,7 @@ var ClaudeCodeInstaller = class {
4959
4954
  }
4960
4955
  /** installed_plugins.json 是否有本 plugin 的 user scope 条目(uninstall dry-run 用) */
4961
4956
  async installedPluginsHasEntry(ref) {
4962
- if (!await pathExists3(this.paths.installedPluginsJson)) {
4957
+ if (!await pathExists(this.paths.installedPluginsJson)) {
4963
4958
  return false;
4964
4959
  }
4965
4960
  const { data } = await readJsonFile(
@@ -4972,7 +4967,7 @@ var ClaudeCodeInstaller = class {
4972
4967
  }
4973
4968
  /** settings.json.enabledPlugins 是否有本 key(uninstall dry-run 用) */
4974
4969
  async settingsHasEnabledKey(key) {
4975
- if (!await pathExists3(this.paths.settingsJson)) {
4970
+ if (!await pathExists(this.paths.settingsJson)) {
4976
4971
  return false;
4977
4972
  }
4978
4973
  const { data } = await readJsonFile(
@@ -4999,7 +4994,7 @@ var ClaudeCodeInstaller = class {
4999
4994
  return Object.keys(servers);
5000
4995
  }
5001
4996
  async findLatestManifestPath(ref) {
5002
- if (!await pathExists3(this.paths.installedPluginsJson)) {
4997
+ if (!await pathExists(this.paths.installedPluginsJson)) {
5003
4998
  return null;
5004
4999
  }
5005
5000
  const { data } = await readJsonFile(
@@ -5015,7 +5010,7 @@ var ClaudeCodeInstaller = class {
5015
5010
  ".claude-plugin",
5016
5011
  "plugin.json"
5017
5012
  );
5018
- if (await pathExists3(manifestPath)) {
5013
+ if (await pathExists(manifestPath)) {
5019
5014
  return manifestPath;
5020
5015
  }
5021
5016
  return null;
@@ -5042,7 +5037,7 @@ function buildPluginJson(plugin, mcpServers) {
5042
5037
  }
5043
5038
  async function readMarketplaceCacheSource(metaPath) {
5044
5039
  try {
5045
- const raw = await readFile6(metaPath, "utf8");
5040
+ const raw = await readFile5(metaPath, "utf8");
5046
5041
  const parsed = JSON.parse(raw);
5047
5042
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
5048
5043
  const sourceRaw = parsed.sourceRaw;
@@ -5077,7 +5072,7 @@ async function defaultSymlinkRunner(target, linkPath) {
5077
5072
  }
5078
5073
  async function dirExists(p) {
5079
5074
  try {
5080
- const s = await stat3(p);
5075
+ const s = await stat2(p);
5081
5076
  return s.isDirectory();
5082
5077
  } catch {
5083
5078
  return false;
@@ -5117,12 +5112,12 @@ async function copyDirTracked(srcDir, dstDir) {
5117
5112
  const srcPath = join9(srcDir, entry.name);
5118
5113
  const dstPath = join9(dstDir, entry.name);
5119
5114
  if (entry.isDirectory()) {
5120
- await mkdir5(dstPath, { recursive: true });
5115
+ await mkdir4(dstPath, { recursive: true });
5121
5116
  written.push(dstPath);
5122
5117
  const subWritten = await copyDirTracked(srcPath, dstPath);
5123
5118
  written.push(...subWritten);
5124
5119
  } else if (entry.isFile()) {
5125
- await mkdir5(dirname5(dstPath), { recursive: true });
5120
+ await mkdir4(dirname4(dstPath), { recursive: true });
5126
5121
  await copyFile(srcPath, dstPath);
5127
5122
  written.push(dstPath);
5128
5123
  }
@@ -5133,7 +5128,7 @@ async function writeAtomicWithMtimeCheck(filePath, data, expectedMtimeMs) {
5133
5128
  if (expectedMtimeMs !== null) {
5134
5129
  let currentMtime = null;
5135
5130
  try {
5136
- const s = await stat3(filePath);
5131
+ const s = await stat2(filePath);
5137
5132
  currentMtime = s.mtimeMs;
5138
5133
  } catch {
5139
5134
  currentMtime = null;
@@ -5156,19 +5151,27 @@ async function retryOnConflict(fn) {
5156
5151
  }
5157
5152
 
5158
5153
  // src/installers/codex/installer.ts
5159
- import { constants as fsConstants7 } from "fs";
5154
+ import { constants as fsConstants6 } from "fs";
5160
5155
  import {
5161
- access as access7,
5156
+ access as access6,
5162
5157
  cp,
5163
- mkdir as mkdir7,
5158
+ mkdir as mkdir6,
5159
+ mkdtemp,
5164
5160
  readdir as readdir3,
5165
- readFile as readFile8,
5166
- rm as rm7,
5167
- stat as stat5,
5168
- writeFile as writeFile6
5161
+ readFile as readFile7,
5162
+ rename as rename5,
5163
+ rm as rm6,
5164
+ stat as stat4,
5165
+ writeFile as writeFile5
5169
5166
  } from "fs/promises";
5170
5167
  import { homedir as homedir6 } from "os";
5171
- import { dirname as dirname7, relative as pathRelative, resolve as resolve10 } from "path";
5168
+ import {
5169
+ basename as basename2,
5170
+ dirname as dirname6,
5171
+ extname,
5172
+ relative as pathRelative,
5173
+ resolve as resolve10
5174
+ } from "path";
5172
5175
 
5173
5176
  // src/installers/codex/mcp.ts
5174
5177
  import { isAbsolute as isAbsolute3 } from "path";
@@ -5223,6 +5226,18 @@ function codexConfigTomlPath(home) {
5223
5226
  function codexPluginsCacheDir(home) {
5224
5227
  return pathResolve(codexHomeDir(home), "plugins", "cache");
5225
5228
  }
5229
+ function codexPluginsMarketplacesDir(home) {
5230
+ return pathResolve(codexHomeDir(home), "plugins", "marketplaces");
5231
+ }
5232
+ function codexMarketplaceDir(home, marketplace) {
5233
+ return pathResolve(codexPluginsMarketplacesDir(home), marketplace);
5234
+ }
5235
+ function codexMarketplaceManifestPath(marketplaceDir) {
5236
+ return pathResolve(marketplaceDir, ".agents", "plugins", "marketplace.json");
5237
+ }
5238
+ function codexMarketplacePluginDir(marketplaceDir, pluginName) {
5239
+ return pathResolve(marketplaceDir, "plugins", pluginName);
5240
+ }
5226
5241
  function codexPluginVersionDir(home, ref, version) {
5227
5242
  return pathResolve(
5228
5243
  codexPluginsCacheDir(home),
@@ -5259,19 +5274,19 @@ function pluginSectionKey(ref) {
5259
5274
  }
5260
5275
 
5261
5276
  // src/installers/codex/toml.ts
5262
- import { randomUUID as randomUUID5 } from "crypto";
5263
- import { constants as fsConstants6 } from "fs";
5277
+ import { randomUUID as randomUUID4 } from "crypto";
5278
+ import { constants as fsConstants5 } from "fs";
5264
5279
  import {
5265
- access as access6,
5280
+ access as access5,
5266
5281
  copyFile as copyFile2,
5267
- mkdir as mkdir6,
5268
- readFile as readFile7,
5269
- rename as rename5,
5270
- rm as rm6,
5271
- stat as stat4,
5272
- writeFile as writeFile5
5282
+ mkdir as mkdir5,
5283
+ readFile as readFile6,
5284
+ rename as rename4,
5285
+ rm as rm5,
5286
+ stat as stat3,
5287
+ writeFile as writeFile4
5273
5288
  } from "fs/promises";
5274
- import { dirname as dirname6 } from "path";
5289
+ import { dirname as dirname5 } from "path";
5275
5290
  import tomlModule from "@iarna/toml";
5276
5291
  var { parse: tomlParse, stringify: tomlStringify } = tomlModule;
5277
5292
  var CodexConfigTomlError = class extends Error {
@@ -5305,8 +5320,8 @@ async function readCodexConfig(filePath) {
5305
5320
  if (!await pathExists4(filePath)) {
5306
5321
  return { data: {}, mtimeMs: null };
5307
5322
  }
5308
- const stats = await stat4(filePath);
5309
- const raw = await readFile7(filePath, "utf8");
5323
+ const stats = await stat3(filePath);
5324
+ const raw = await readFile6(filePath, "utf8");
5310
5325
  try {
5311
5326
  const parsed = tomlParse(raw);
5312
5327
  return { data: parsed, mtimeMs: stats.mtimeMs };
@@ -5320,7 +5335,7 @@ async function backupCodexConfig(filePath, backupPath) {
5320
5335
  }
5321
5336
  let finalBackup = backupPath;
5322
5337
  if (await pathExists4(finalBackup)) {
5323
- finalBackup = `${backupPath}.${randomUUID5().slice(0, 8)}`;
5338
+ finalBackup = `${backupPath}.${randomUUID4().slice(0, 8)}`;
5324
5339
  }
5325
5340
  await copyFile2(filePath, finalBackup);
5326
5341
  return finalBackup;
@@ -5329,19 +5344,19 @@ async function writeCodexConfig(filePath, data, expectedMtimeMs) {
5329
5344
  await assertNoMtimeDrift(filePath, expectedMtimeMs);
5330
5345
  const serialized = tomlStringify(data);
5331
5346
  await atomicWrite2(filePath, serialized);
5332
- const afterStats = await stat4(filePath);
5347
+ const afterStats = await stat3(filePath);
5333
5348
  return { mtimeMs: afterStats.mtimeMs };
5334
5349
  }
5335
5350
  async function restoreCodexConfigFromBackup(filePath, backupPath) {
5336
5351
  if (!backupPath || !await pathExists4(backupPath)) {
5337
5352
  if (await pathExists4(filePath)) {
5338
- await rm6(filePath, { force: true });
5353
+ await rm5(filePath, { force: true });
5339
5354
  }
5340
5355
  return;
5341
5356
  }
5342
- const raw = await readFile7(backupPath, "utf8");
5357
+ const raw = await readFile6(backupPath, "utf8");
5343
5358
  await atomicWrite2(filePath, raw);
5344
- await rm6(backupPath, { force: true }).catch(() => {
5359
+ await rm5(backupPath, { force: true }).catch(() => {
5345
5360
  });
5346
5361
  }
5347
5362
  function setMarketplaceSection(config, name, entry) {
@@ -5380,20 +5395,20 @@ function marketplaceHasOtherPlugins(config, marketplaceName, excludedPluginKey)
5380
5395
  }
5381
5396
  async function pathExists4(p) {
5382
5397
  try {
5383
- await access6(p, fsConstants6.F_OK);
5398
+ await access5(p, fsConstants5.F_OK);
5384
5399
  return true;
5385
5400
  } catch {
5386
5401
  return false;
5387
5402
  }
5388
5403
  }
5389
5404
  async function atomicWrite2(filePath, content) {
5390
- await mkdir6(dirname6(filePath), { recursive: true });
5391
- const tmp = `${filePath}.${randomUUID5()}.tmp`;
5405
+ await mkdir5(dirname5(filePath), { recursive: true });
5406
+ const tmp = `${filePath}.${randomUUID4()}.tmp`;
5392
5407
  try {
5393
- await writeFile5(tmp, content, { encoding: "utf8", flag: "w" });
5394
- await rename5(tmp, filePath);
5408
+ await writeFile4(tmp, content, { encoding: "utf8", flag: "w" });
5409
+ await rename4(tmp, filePath);
5395
5410
  } catch (err) {
5396
- await rm6(tmp, { force: true }).catch(() => {
5411
+ await rm5(tmp, { force: true }).catch(() => {
5397
5412
  });
5398
5413
  throw err;
5399
5414
  }
@@ -5405,7 +5420,7 @@ async function assertNoMtimeDrift(filePath, expectedMtimeMs) {
5405
5420
  }
5406
5421
  return;
5407
5422
  }
5408
- const current = await stat4(filePath).catch(() => null);
5423
+ const current = await stat3(filePath).catch(() => null);
5409
5424
  if (current === null) {
5410
5425
  throw new CodexConfigTomlConflictError(filePath);
5411
5426
  }
@@ -5429,7 +5444,7 @@ function ensureObjectSection(config, key) {
5429
5444
  }
5430
5445
 
5431
5446
  // src/installers/codex/installer.ts
5432
- var CODEX_UNSUPPORTED = ["commands", "rules", "hooks"];
5447
+ var CODEX_UNSUPPORTED = ["rules", "hooks"];
5433
5448
  var CodexInstallError = class extends Error {
5434
5449
  constructor(message) {
5435
5450
  super(message);
@@ -5536,6 +5551,7 @@ var CodexInstaller = class {
5536
5551
  }
5537
5552
  const rollback = {
5538
5553
  createdVersionDir: null,
5554
+ createdMarketplacePluginDir: null,
5539
5555
  backupPath: null,
5540
5556
  preExistingConfig: null
5541
5557
  };
@@ -5650,19 +5666,23 @@ var CodexInstaller = class {
5650
5666
  const ref = plugin.ref;
5651
5667
  const key = pluginSectionKey(ref);
5652
5668
  const versionDir = codexPluginVersionDir(this.home, ref, plugin.version);
5669
+ const marketplaceDir = codexMarketplaceDir(this.home, ref.marketplace);
5670
+ const marketplacePluginDir = codexMarketplacePluginDir(
5671
+ marketplaceDir,
5672
+ ref.name
5673
+ );
5653
5674
  const cfgPath = codexConfigTomlPath(this.home);
5654
5675
  const preExisted = await pathExists5(versionDir);
5655
5676
  if (preExisted) {
5656
- await rm7(versionDir, { recursive: true, force: true });
5677
+ await rm6(versionDir, { recursive: true, force: true });
5657
5678
  }
5658
- await mkdir7(versionDir, { recursive: true });
5679
+ await mkdir6(versionDir, { recursive: true });
5659
5680
  rollback.createdVersionDir = versionDir;
5660
- const srcSkills = resolve10(plugin.sourceDir, "skills");
5661
5681
  const dstSkills = codexPluginSkillsDir(versionDir);
5662
- const hasSkills2 = await hasNonEmptyDir(srcSkills);
5663
5682
  const writtenFiles = [versionDir];
5664
- if (hasSkills2) {
5665
- await cp(srcSkills, dstSkills, { recursive: true });
5683
+ const skillsPlan = await planCodexSkills(plugin);
5684
+ if (skillsPlan.hasSkills) {
5685
+ await materializeCodexSkills(skillsPlan, dstSkills);
5666
5686
  writtenFiles.push(dstSkills);
5667
5687
  }
5668
5688
  let mcpJsonRef;
@@ -5712,11 +5732,10 @@ var CodexInstaller = class {
5712
5732
  rollback.backupPath = actualBackup;
5713
5733
  const { data: config, mtimeMs } = await readCodexConfig(cfgPath);
5714
5734
  rollback.preExistingConfig = mtimeMs !== null;
5715
- const marketplaceSource = this.resolveMarketplaceSource(plugin);
5716
5735
  setMarketplaceSection(config, ref.marketplace, {
5717
5736
  last_updated: this.now().toISOString(),
5718
- source_type: marketplaceSource.sourceType,
5719
- source: marketplaceSource.source
5737
+ source_type: "local",
5738
+ source: marketplaceDir
5720
5739
  });
5721
5740
  setPluginEntry(config, key, { enabled: true });
5722
5741
  await writeCodexConfig(cfgPath, config, mtimeMs);
@@ -5724,6 +5743,18 @@ var CodexInstaller = class {
5724
5743
  if (actualBackup) {
5725
5744
  writtenFiles.push(actualBackup);
5726
5745
  }
5746
+ await writeCodexMarketplaceMirror({
5747
+ marketplaceDir,
5748
+ pluginDir: marketplacePluginDir,
5749
+ plugin,
5750
+ versionDir
5751
+ });
5752
+ rollback.createdMarketplacePluginDir = marketplacePluginDir;
5753
+ writtenFiles.push(
5754
+ marketplaceDir,
5755
+ codexMarketplaceManifestPath(marketplaceDir),
5756
+ marketplacePluginDir
5757
+ );
5727
5758
  const included = computeIncludedCapabilities(plugin);
5728
5759
  const notes = [];
5729
5760
  if (actualBackup) {
@@ -5755,10 +5786,22 @@ var CodexInstaller = class {
5755
5786
  async buildDryRunResult(plugin) {
5756
5787
  const ref = plugin.ref;
5757
5788
  const versionDir = codexPluginVersionDir(this.home, ref, plugin.version);
5789
+ const marketplaceDir = codexMarketplaceDir(this.home, ref.marketplace);
5790
+ const marketplacePluginDir = codexMarketplacePluginDir(
5791
+ marketplaceDir,
5792
+ ref.name
5793
+ );
5758
5794
  const cfgPath = codexConfigTomlPath(this.home);
5759
- const files = [versionDir, codexPluginManifestPath(versionDir)];
5760
- const srcSkills = resolve10(plugin.sourceDir, "skills");
5761
- if (await hasNonEmptyDir(srcSkills)) {
5795
+ const files = [
5796
+ versionDir,
5797
+ codexPluginManifestPath(versionDir),
5798
+ marketplaceDir,
5799
+ codexMarketplaceManifestPath(marketplaceDir),
5800
+ marketplacePluginDir,
5801
+ cfgPath
5802
+ ];
5803
+ const skillsPlan = await planCodexSkills(plugin);
5804
+ if (skillsPlan.hasSkills) {
5762
5805
  files.push(codexPluginSkillsDir(versionDir));
5763
5806
  }
5764
5807
  let mcpKeys = [];
@@ -5781,7 +5824,7 @@ var CodexInstaller = class {
5781
5824
  );
5782
5825
  } else {
5783
5826
  try {
5784
- const raw = await readFile8(srcPath, "utf8");
5827
+ const raw = await readFile7(srcPath, "utf8");
5785
5828
  const parsed = JSON.parse(raw);
5786
5829
  if (parsed.mcpServers && typeof parsed.mcpServers === "object") {
5787
5830
  mcpKeys = Object.keys(parsed.mcpServers).sort();
@@ -5801,7 +5844,6 @@ var CodexInstaller = class {
5801
5844
  }
5802
5845
  }
5803
5846
  }
5804
- files.push(cfgPath);
5805
5847
  return {
5806
5848
  target: "codex",
5807
5849
  status: dryRunStatus,
@@ -5812,16 +5854,16 @@ var CodexInstaller = class {
5812
5854
  ...dryRunErrors.length > 0 ? { errors: dryRunErrors } : {}
5813
5855
  };
5814
5856
  }
5815
- resolveMarketplaceSource(plugin) {
5816
- if (this.marketplaceSource) {
5817
- return this.marketplaceSource;
5818
- }
5819
- return { sourceType: "local", source: plugin.sourceDir };
5820
- }
5821
5857
  };
5822
5858
  function computeIncludedCapabilities(plugin) {
5823
- const supported = ["skills", "mcp"];
5824
- return supported.filter((cap) => plugin.capabilities.includes(cap));
5859
+ const included = [];
5860
+ if (plugin.capabilities.includes("skills") || plugin.capabilities.includes("commands")) {
5861
+ included.push("skills");
5862
+ }
5863
+ if (plugin.capabilities.includes("mcp")) {
5864
+ included.push("mcp");
5865
+ }
5866
+ return included;
5825
5867
  }
5826
5868
  function buildCodexPluginJson(manifest, mcpServersRef) {
5827
5869
  const out = {
@@ -5838,9 +5880,28 @@ function buildCodexPluginJson(manifest, mcpServersRef) {
5838
5880
  if (mcpServersRef !== void 0) {
5839
5881
  out.mcpServers = mcpServersRef;
5840
5882
  }
5841
- if (manifest.interface !== void 0) out.interface = manifest.interface;
5883
+ out.interface = buildCodexInterfaceMetadata(manifest);
5842
5884
  return out;
5843
5885
  }
5886
+ function buildCodexInterfaceMetadata(manifest) {
5887
+ const existing = manifest.interface ?? {};
5888
+ return {
5889
+ displayName: existing.displayName ?? manifest.name,
5890
+ shortDescription: existing.shortDescription ?? manifest.description ?? manifest.name,
5891
+ longDescription: existing.longDescription ?? manifest.description,
5892
+ developerName: existing.developerName ?? manifest.author?.name,
5893
+ category: existing.category ?? "Engineering",
5894
+ capabilities: existing.capabilities ?? ["Read", "Write"],
5895
+ websiteURL: existing.websiteURL ?? manifest.homepage,
5896
+ privacyPolicyURL: existing.privacyPolicyURL,
5897
+ termsOfServiceURL: existing.termsOfServiceURL,
5898
+ defaultPrompt: existing.defaultPrompt,
5899
+ brandColor: existing.brandColor,
5900
+ composerIcon: existing.composerIcon,
5901
+ logo: existing.logo,
5902
+ screenshots: existing.screenshots
5903
+ };
5904
+ }
5844
5905
  async function rollbackInstall(home, rollback) {
5845
5906
  const cfgPath = codexConfigTomlPath(home);
5846
5907
  if (rollback.backupPath) {
@@ -5849,11 +5910,18 @@ async function rollbackInstall(home, rollback) {
5849
5910
  }
5850
5911
  );
5851
5912
  } else if (rollback.preExistingConfig === false) {
5852
- await rm7(cfgPath, { force: true }).catch(() => {
5913
+ await rm6(cfgPath, { force: true }).catch(() => {
5853
5914
  });
5854
5915
  }
5855
5916
  if (rollback.createdVersionDir) {
5856
- await rm7(rollback.createdVersionDir, {
5917
+ await rm6(rollback.createdVersionDir, {
5918
+ recursive: true,
5919
+ force: true
5920
+ }).catch(() => {
5921
+ });
5922
+ }
5923
+ if (rollback.createdMarketplacePluginDir) {
5924
+ await rm6(rollback.createdMarketplacePluginDir, {
5857
5925
  recursive: true,
5858
5926
  force: true
5859
5927
  }).catch(() => {
@@ -5898,8 +5966,8 @@ async function copyAuthorProvidedMcp(sourceDir, versionDir, relativeRef) {
5898
5966
  }
5899
5967
  if (!await pathExists5(srcPath)) return null;
5900
5968
  const destPath = resolve10(versionDir, relativeRef);
5901
- await mkdir7(dirname7(destPath), { recursive: true });
5902
- const raw = await readFile8(srcPath, "utf8");
5969
+ await mkdir6(dirname6(destPath), { recursive: true });
5970
+ const raw = await readFile7(srcPath, "utf8");
5903
5971
  await writeFileAtomic(destPath, raw);
5904
5972
  let mcpKeys = [];
5905
5973
  try {
@@ -5912,22 +5980,380 @@ async function copyAuthorProvidedMcp(sourceDir, versionDir, relativeRef) {
5912
5980
  }
5913
5981
  return { destPath, mcpKeys };
5914
5982
  }
5983
+ async function writeCodexMarketplaceMirror(input) {
5984
+ const pluginParent = dirname6(input.pluginDir);
5985
+ const pluginBase = basename2(input.pluginDir);
5986
+ await mkdir6(pluginParent, { recursive: true });
5987
+ const tmpPluginDir = await mkdtemp(
5988
+ resolve10(pluginParent, `.${pluginBase}.tmp-`)
5989
+ );
5990
+ const backupPluginDir = await mkdtemp(
5991
+ resolve10(pluginParent, `.${pluginBase}.bak-`)
5992
+ );
5993
+ let hadExistingPluginDir = false;
5994
+ let swappedPluginDir = false;
5995
+ try {
5996
+ await rm6(tmpPluginDir, { recursive: true, force: true });
5997
+ await rm6(backupPluginDir, { recursive: true, force: true }).catch(() => {
5998
+ });
5999
+ await cp(input.versionDir, tmpPluginDir, {
6000
+ recursive: true,
6001
+ dereference: false,
6002
+ preserveTimestamps: true,
6003
+ verbatimSymlinks: true
6004
+ });
6005
+ hadExistingPluginDir = await pathExists5(input.pluginDir);
6006
+ if (hadExistingPluginDir) {
6007
+ await rename5(input.pluginDir, backupPluginDir);
6008
+ }
6009
+ await rename5(tmpPluginDir, input.pluginDir);
6010
+ swappedPluginDir = true;
6011
+ await upsertCodexMarketplaceManifest(input.marketplaceDir, input.plugin);
6012
+ if (hadExistingPluginDir) {
6013
+ await rm6(backupPluginDir, { recursive: true, force: true });
6014
+ }
6015
+ } catch (err) {
6016
+ await rm6(tmpPluginDir, { recursive: true, force: true }).catch(() => {
6017
+ });
6018
+ let restoredBackup = false;
6019
+ if (hadExistingPluginDir && await pathExists5(backupPluginDir)) {
6020
+ let displacedPluginDir = null;
6021
+ try {
6022
+ if (await pathExists5(input.pluginDir)) {
6023
+ displacedPluginDir = await mkdtemp(
6024
+ resolve10(pluginParent, `.${pluginBase}.failed-`)
6025
+ );
6026
+ await rm6(displacedPluginDir, { recursive: true, force: true });
6027
+ await rename5(input.pluginDir, displacedPluginDir);
6028
+ }
6029
+ await rename5(backupPluginDir, input.pluginDir);
6030
+ restoredBackup = true;
6031
+ if (displacedPluginDir) {
6032
+ await rm6(displacedPluginDir, { recursive: true, force: true }).catch(
6033
+ () => {
6034
+ }
6035
+ );
6036
+ }
6037
+ } catch {
6038
+ if (displacedPluginDir && !await pathExists5(input.pluginDir) && await pathExists5(displacedPluginDir)) {
6039
+ await rename5(displacedPluginDir, input.pluginDir).catch(() => {
6040
+ });
6041
+ }
6042
+ }
6043
+ } else if (swappedPluginDir) {
6044
+ await rm6(input.pluginDir, { recursive: true, force: true }).catch(
6045
+ () => {
6046
+ }
6047
+ );
6048
+ }
6049
+ if (restoredBackup || !hadExistingPluginDir) {
6050
+ await rm6(backupPluginDir, { recursive: true, force: true }).catch(
6051
+ () => {
6052
+ }
6053
+ );
6054
+ }
6055
+ throw err;
6056
+ }
6057
+ }
6058
+ async function upsertCodexMarketplaceManifest(marketplaceDir, plugin) {
6059
+ const manifestPath = codexMarketplaceManifestPath(marketplaceDir);
6060
+ const manifest = await readCodexMarketplaceManifest(manifestPath, {
6061
+ name: plugin.ref.marketplace,
6062
+ interface: {
6063
+ displayName: plugin.ref.marketplace
6064
+ },
6065
+ plugins: []
6066
+ });
6067
+ const plugins = Array.isArray(manifest.plugins) ? manifest.plugins : [];
6068
+ const nextEntry = {
6069
+ name: plugin.ref.name,
6070
+ source: {
6071
+ source: "local",
6072
+ path: `./plugins/${plugin.ref.name}`
6073
+ },
6074
+ policy: {
6075
+ installation: "AVAILABLE",
6076
+ authentication: "ON_INSTALL"
6077
+ },
6078
+ category: buildCodexInterfaceMetadata(plugin.manifest).category
6079
+ };
6080
+ const nextPlugins = [
6081
+ ...plugins.filter((entry) => entry?.name !== plugin.ref.name),
6082
+ nextEntry
6083
+ ].sort((a, b) => a.name.localeCompare(b.name));
6084
+ await writeFileAtomic(
6085
+ manifestPath,
6086
+ `${JSON.stringify({ ...manifest, plugins: nextPlugins }, null, 2)}
6087
+ `
6088
+ );
6089
+ }
6090
+ async function readCodexMarketplaceManifest(manifestPath, fallback) {
6091
+ if (!await pathExists5(manifestPath)) return fallback;
6092
+ try {
6093
+ const parsed = JSON.parse(await readFile7(manifestPath, "utf8"));
6094
+ return {
6095
+ name: typeof parsed.name === "string" ? parsed.name : fallback.name,
6096
+ interface: parsed.interface && typeof parsed.interface === "object" && !Array.isArray(parsed.interface) ? parsed.interface : fallback.interface,
6097
+ plugins: Array.isArray(parsed.plugins) ? parsed.plugins.filter(isMarketplacePluginEntry) : fallback.plugins
6098
+ };
6099
+ } catch {
6100
+ return fallback;
6101
+ }
6102
+ }
6103
+ function isMarketplacePluginEntry(value) {
6104
+ return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.name === "string";
6105
+ }
6106
+ async function planCodexSkills(plugin) {
6107
+ const sources = [];
6108
+ const usedNames = /* @__PURE__ */ new Set();
6109
+ const coveredAliases = /* @__PURE__ */ new Set();
6110
+ const commandFiles = await listMarkdownFiles(
6111
+ resolve10(plugin.sourceDir, "commands"),
6112
+ {
6113
+ recursive: false
6114
+ }
6115
+ );
6116
+ const commandAliases = new Set(
6117
+ await Promise.all(
6118
+ commandFiles.map((file2) => markdownSkillAlias(plugin.manifest.name, file2))
6119
+ )
6120
+ );
6121
+ const nativeSkillsDir = resolve10(plugin.sourceDir, "skills");
6122
+ if (await hasCodexSkillDir(nativeSkillsDir)) {
6123
+ for (const name of await listCodexSkillNames(nativeSkillsDir)) {
6124
+ usedNames.add(name);
6125
+ coveredAliases.add(skillAlias(plugin.manifest.name, name));
6126
+ sources.push({
6127
+ kind: "copy-skill",
6128
+ sourceDir: resolve10(nativeSkillsDir, name),
6129
+ skillName: name
6130
+ });
6131
+ }
6132
+ } else {
6133
+ const dotSkillsDir = resolve10(plugin.sourceDir, ".skills");
6134
+ const claudeSkills = await listMarkdownFiles(dotSkillsDir);
6135
+ const claudeAliases = /* @__PURE__ */ new Map();
6136
+ for (const file2 of claudeSkills) {
6137
+ const alias = await markdownSkillAlias(plugin.manifest.name, file2);
6138
+ claudeAliases.set(alias, (claudeAliases.get(alias) ?? 0) + 1);
6139
+ }
6140
+ for (const file2 of claudeSkills) {
6141
+ const alias = await markdownSkillAlias(plugin.manifest.name, file2);
6142
+ if (commandAliases.has(alias)) {
6143
+ continue;
6144
+ }
6145
+ const uniqueAlias = (claudeAliases.get(alias) ?? 0) > 1 ? markdownPathAlias(plugin.manifest.name, dotSkillsDir, file2) : alias;
6146
+ const skillName = uniqueSkillName(
6147
+ usedNames,
6148
+ `${plugin.manifest.name}-${uniqueAlias}`
6149
+ );
6150
+ coveredAliases.add(alias);
6151
+ coveredAliases.add(uniqueAlias);
6152
+ sources.push({ kind: "markdown", sourceFile: file2, skillName });
6153
+ }
6154
+ }
6155
+ for (const file2 of commandFiles) {
6156
+ const alias = await markdownSkillAlias(plugin.manifest.name, file2);
6157
+ if (coveredAliases.has(alias)) {
6158
+ continue;
6159
+ }
6160
+ const skillName = uniqueSkillName(
6161
+ usedNames,
6162
+ `${plugin.manifest.name}-${alias}`
6163
+ );
6164
+ coveredAliases.add(alias);
6165
+ sources.push({ kind: "markdown", sourceFile: file2, skillName });
6166
+ }
6167
+ return { sources, hasSkills: sources.length > 0 };
6168
+ }
6169
+ async function materializeCodexSkills(plan, dstSkills) {
6170
+ for (const source of plan.sources) {
6171
+ if (source.kind === "copy-skill") {
6172
+ await cp(source.sourceDir, resolve10(dstSkills, source.skillName), {
6173
+ recursive: true,
6174
+ dereference: false,
6175
+ preserveTimestamps: true,
6176
+ verbatimSymlinks: true
6177
+ });
6178
+ continue;
6179
+ }
6180
+ const raw = await readFile7(source.sourceFile, "utf8");
6181
+ const normalized = normalizeSkillMarkdown(raw, source.skillName);
6182
+ await writeFileAtomic(
6183
+ resolve10(dstSkills, source.skillName, "SKILL.md"),
6184
+ normalized
6185
+ );
6186
+ }
6187
+ }
6188
+ function normalizeSkillMarkdown(raw, skillName) {
6189
+ const text = raw.replace(/\r\n/g, "\n");
6190
+ const parsed = splitMarkdownFrontmatter(text);
6191
+ if (parsed) {
6192
+ const lines = parsed.frontmatter.split("\n");
6193
+ const hasName = lines.some((line) => /^name:\s*/.test(line));
6194
+ const nextLines = hasName ? lines.map(
6195
+ (line) => /^name:\s*/.test(line) ? `name: ${skillName}` : line
6196
+ ) : [`name: ${skillName}`, ...lines];
6197
+ const frontmatter = nextLines.join("\n");
6198
+ const next = `---
6199
+ ${frontmatter}${frontmatter.endsWith("\n") ? "" : "\n"}${parsed.afterFrontmatter}`;
6200
+ return next.endsWith("\n") ? next : `${next}
6201
+ `;
6202
+ }
6203
+ return `---
6204
+ name: ${skillName}
6205
+ description: ${firstMarkdownLine(raw) ?? skillName}
6206
+ ---
6207
+
6208
+ ${text.endsWith("\n") ? text : `${text}
6209
+ `}`;
6210
+ }
6211
+ function splitMarkdownFrontmatter(text) {
6212
+ if (!text.startsWith("---\n")) return null;
6213
+ const lines = text.split("\n");
6214
+ if (lines[0] !== "---") return null;
6215
+ let blockScalarIndent = null;
6216
+ for (let i = 1; i < lines.length; i += 1) {
6217
+ const line = lines[i] ?? "";
6218
+ const indent = leadingSpaces(line);
6219
+ if (blockScalarIndent !== null) {
6220
+ if (line.trim() === "" || indent > blockScalarIndent) {
6221
+ continue;
6222
+ }
6223
+ blockScalarIndent = null;
6224
+ }
6225
+ if (line === "---") {
6226
+ return {
6227
+ frontmatter: lines.slice(1, i).join("\n"),
6228
+ afterFrontmatter: lines.slice(i).join("\n")
6229
+ };
6230
+ }
6231
+ if (/^[A-Za-z0-9_.-]+:\s*[|>]/.test(line)) {
6232
+ blockScalarIndent = indent;
6233
+ }
6234
+ }
6235
+ return null;
6236
+ }
6237
+ function leadingSpaces(value) {
6238
+ const match = value.match(/^ */);
6239
+ return match?.[0].length ?? 0;
6240
+ }
6241
+ function frontmatterValue(frontmatter, key) {
6242
+ for (const line of frontmatter.split("\n")) {
6243
+ const match = line.match(/^([A-Za-z0-9_.-]+):\s*(.*)$/);
6244
+ if (match?.[1] === key) {
6245
+ return match[2]?.trim() ?? "";
6246
+ }
6247
+ }
6248
+ return null;
6249
+ }
6250
+ function firstMarkdownLine(raw) {
6251
+ const line = raw.split(/\r?\n/).map((value) => value.trim()).find((value) => value.length > 0 && value !== "---");
6252
+ if (!line) return null;
6253
+ return line.replace(/^#+\s*/, "").slice(0, 140);
6254
+ }
6255
+ async function hasCodexSkillDir(skillsDir) {
6256
+ return (await listCodexSkillNames(skillsDir)).length > 0;
6257
+ }
6258
+ async function listCodexSkillNames(skillsDir) {
6259
+ if (!await isDir(skillsDir)) return [];
6260
+ let entries;
6261
+ try {
6262
+ entries = await readdir3(skillsDir, { withFileTypes: true });
6263
+ } catch {
6264
+ return [];
6265
+ }
6266
+ const names = [];
6267
+ for (const entry of entries) {
6268
+ if (!entry.isDirectory()) continue;
6269
+ if (await pathExists5(resolve10(skillsDir, entry.name, "SKILL.md"))) {
6270
+ names.push(entry.name);
6271
+ }
6272
+ }
6273
+ return names.sort();
6274
+ }
6275
+ async function listMarkdownFiles(dir, opts = {}) {
6276
+ if (!await isDir(dir)) return [];
6277
+ const recursive = opts.recursive !== false;
6278
+ const out = [];
6279
+ async function walk2(current) {
6280
+ let entries;
6281
+ try {
6282
+ entries = await readdir3(current, { withFileTypes: true });
6283
+ } catch {
6284
+ return;
6285
+ }
6286
+ for (const entry of entries) {
6287
+ const abs = resolve10(current, entry.name);
6288
+ if (entry.isDirectory()) {
6289
+ if (recursive) await walk2(abs);
6290
+ continue;
6291
+ }
6292
+ if (entry.isFile() && extname(entry.name).toLowerCase() === ".md") {
6293
+ out.push(abs);
6294
+ }
6295
+ }
6296
+ }
6297
+ await walk2(dir);
6298
+ return out.sort();
6299
+ }
6300
+ function uniqueSkillName(used, desired) {
6301
+ const base = desired.length > 0 ? desired : "skill";
6302
+ let candidate = base;
6303
+ let i = 2;
6304
+ while (used.has(candidate)) {
6305
+ candidate = `${base}-${i}`;
6306
+ i += 1;
6307
+ }
6308
+ used.add(candidate);
6309
+ return candidate;
6310
+ }
6311
+ async function markdownSkillAlias(pluginName, filePath) {
6312
+ const triggerAlias = extractTriggerAlias(await readFile7(filePath, "utf8"));
6313
+ return skillAlias(
6314
+ pluginName,
6315
+ triggerAlias ?? basename2(filePath, extname(filePath))
6316
+ );
6317
+ }
6318
+ function markdownPathAlias(pluginName, baseDir, filePath) {
6319
+ const rel = pathRelative(baseDir, filePath);
6320
+ return skillAlias(pluginName, rel.slice(0, -extname(rel).length));
6321
+ }
6322
+ function skillAlias(pluginName, value) {
6323
+ let alias = slugify(value);
6324
+ const pluginPrefix = `${slugify(pluginName)}-`;
6325
+ if (alias.startsWith(pluginPrefix)) {
6326
+ alias = alias.slice(pluginPrefix.length);
6327
+ }
6328
+ return alias.length > 0 ? alias : "skill";
6329
+ }
6330
+ function extractTriggerAlias(raw) {
6331
+ const parsed = splitMarkdownFrontmatter(raw.replace(/\r\n/g, "\n"));
6332
+ const trigger = parsed ? frontmatterValue(parsed.frontmatter, "trigger") : null;
6333
+ if (!trigger) return null;
6334
+ const match = trigger.match(/\/[A-Za-z0-9_.-]+:([A-Za-z0-9_.-]+)/);
6335
+ return match?.[1] ?? null;
6336
+ }
6337
+ function slugify(value) {
6338
+ const slug = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
6339
+ return slug.length > 0 ? slug : "skill";
6340
+ }
5915
6341
  async function writeFileAtomic(filePath, content) {
5916
- await mkdir7(dirname7(filePath), { recursive: true });
6342
+ await mkdir6(dirname6(filePath), { recursive: true });
5917
6343
  const tmp = `${filePath}.${Math.random().toString(36).slice(2)}.tmp`;
5918
6344
  try {
5919
- await writeFile6(tmp, content, { encoding: "utf8", flag: "w" });
6345
+ await writeFile5(tmp, content, { encoding: "utf8", flag: "w" });
5920
6346
  const { rename: rename8 } = await import("fs/promises");
5921
6347
  await rename8(tmp, filePath);
5922
6348
  } catch (err) {
5923
- await rm7(tmp, { force: true }).catch(() => {
6349
+ await rm6(tmp, { force: true }).catch(() => {
5924
6350
  });
5925
6351
  throw err;
5926
6352
  }
5927
6353
  }
5928
6354
  async function pathExists5(p) {
5929
6355
  try {
5930
- await access7(p, fsConstants7.F_OK);
6356
+ await access6(p, fsConstants6.F_OK);
5931
6357
  return true;
5932
6358
  } catch {
5933
6359
  return false;
@@ -5935,21 +6361,12 @@ async function pathExists5(p) {
5935
6361
  }
5936
6362
  async function isDir(p) {
5937
6363
  try {
5938
- const s = await stat5(p);
6364
+ const s = await stat4(p);
5939
6365
  return s.isDirectory();
5940
6366
  } catch {
5941
6367
  return false;
5942
6368
  }
5943
6369
  }
5944
- async function hasNonEmptyDir(p) {
5945
- if (!await isDir(p)) return false;
5946
- try {
5947
- const entries = await readdir3(p);
5948
- return entries.length > 0;
5949
- } catch {
5950
- return false;
5951
- }
5952
- }
5953
6370
  function dedup(arr) {
5954
6371
  return Array.from(new Set(arr));
5955
6372
  }
@@ -5979,10 +6396,10 @@ async function detectInstalledVersion(home, ref) {
5979
6396
  const manifestPath = resolve10(versionDir, ".codex-plugin", "plugin.json");
5980
6397
  if (!await pathExists5(manifestPath)) continue;
5981
6398
  try {
5982
- const raw = await readFile8(manifestPath, "utf8");
6399
+ const raw = await readFile7(manifestPath, "utf8");
5983
6400
  const obj = JSON.parse(raw);
5984
6401
  const version = typeof obj.version === "string" && obj.version.length > 0 ? obj.version : entry;
5985
- const s = await stat5(versionDir);
6402
+ const s = await stat4(versionDir);
5986
6403
  if (!best || s.mtimeMs > best.mtimeMs) {
5987
6404
  best = { version, mtimeMs: s.mtimeMs };
5988
6405
  }
@@ -6004,20 +6421,20 @@ function defaultRushAiBinaryResolver() {
6004
6421
  }
6005
6422
 
6006
6423
  // src/installers/cursor/installer.ts
6007
- import { randomUUID as randomUUID7 } from "crypto";
6008
- import { constants as fsConstants9 } from "fs";
6424
+ import { randomUUID as randomUUID6 } from "crypto";
6425
+ import { constants as fsConstants8 } from "fs";
6009
6426
  import {
6010
- access as access9,
6427
+ access as access8,
6011
6428
  cp as cp2,
6012
- mkdir as mkdir9,
6013
- readFile as readFile11,
6429
+ mkdir as mkdir8,
6430
+ readFile as readFile10,
6014
6431
  rename as rename7,
6015
- rm as rm9,
6016
- stat as stat7,
6017
- writeFile as writeFile8
6432
+ rm as rm8,
6433
+ stat as stat6,
6434
+ writeFile as writeFile7
6018
6435
  } from "fs/promises";
6019
6436
  import { homedir as homedir7 } from "os";
6020
- import { dirname as dirname9, relative, resolve as resolve13 } from "path";
6437
+ import { dirname as dirname8, relative, resolve as resolve13 } from "path";
6021
6438
 
6022
6439
  // src/installers/cursor/marker.ts
6023
6440
  var RUSH_AI_MARKER = "<!-- rush-ai:auto-generated -->";
@@ -6026,18 +6443,18 @@ function hasRushAiMarker(fileContent) {
6026
6443
  }
6027
6444
 
6028
6445
  // src/installers/cursor/mcp.ts
6029
- import { randomUUID as randomUUID6 } from "crypto";
6030
- import { constants as fsConstants8 } from "fs";
6446
+ import { randomUUID as randomUUID5 } from "crypto";
6447
+ import { constants as fsConstants7 } from "fs";
6031
6448
  import {
6032
- access as access8,
6033
- mkdir as mkdir8,
6034
- readFile as readFile9,
6449
+ access as access7,
6450
+ mkdir as mkdir7,
6451
+ readFile as readFile8,
6035
6452
  rename as rename6,
6036
- rm as rm8,
6037
- stat as stat6,
6038
- writeFile as writeFile7
6453
+ rm as rm7,
6454
+ stat as stat5,
6455
+ writeFile as writeFile6
6039
6456
  } from "fs/promises";
6040
- import { dirname as dirname8 } from "path";
6457
+ import { dirname as dirname7 } from "path";
6041
6458
  async function readCursorMcpJson(filePath) {
6042
6459
  return (await readCursorMcpJsonWithMtime(filePath)).data;
6043
6460
  }
@@ -6045,8 +6462,8 @@ async function readCursorMcpJsonWithMtime(filePath) {
6045
6462
  if (!await pathExists6(filePath)) {
6046
6463
  return { data: {}, mtimeMs: null };
6047
6464
  }
6048
- const stats = await stat6(filePath);
6049
- const raw = await readFile9(filePath, "utf8");
6465
+ const stats = await stat5(filePath);
6466
+ const raw = await readFile8(filePath, "utf8");
6050
6467
  let parsed;
6051
6468
  try {
6052
6469
  parsed = JSON.parse(raw);
@@ -6113,22 +6530,22 @@ function removeMcpServers(current, keysToRemove) {
6113
6530
  return cloned;
6114
6531
  }
6115
6532
  async function writeCursorMcpJson(filePath, data) {
6116
- await mkdir8(dirname8(filePath), { recursive: true });
6533
+ await mkdir7(dirname7(filePath), { recursive: true });
6117
6534
  const content = `${JSON.stringify(data, null, 2)}
6118
6535
  `;
6119
- const tmp = `${filePath}.${randomUUID6()}.tmp`;
6536
+ const tmp = `${filePath}.${randomUUID5()}.tmp`;
6120
6537
  try {
6121
- await writeFile7(tmp, content, { encoding: "utf8", flag: "w" });
6538
+ await writeFile6(tmp, content, { encoding: "utf8", flag: "w" });
6122
6539
  await rename6(tmp, filePath);
6123
6540
  } catch (err) {
6124
- await rm8(tmp, { force: true }).catch(() => {
6541
+ await rm7(tmp, { force: true }).catch(() => {
6125
6542
  });
6126
6543
  throw err;
6127
6544
  }
6128
6545
  }
6129
6546
  async function pathExists6(p) {
6130
6547
  try {
6131
- await access8(p, fsConstants8.F_OK);
6548
+ await access7(p, fsConstants7.F_OK);
6132
6549
  return true;
6133
6550
  } catch {
6134
6551
  return false;
@@ -6264,13 +6681,13 @@ function quoteIfNeeded(value) {
6264
6681
  }
6265
6682
 
6266
6683
  // src/installers/cursor/skills.ts
6267
- import { readFile as readFile10 } from "fs/promises";
6684
+ import { readFile as readFile9 } from "fs/promises";
6268
6685
  import { resolve as resolve12 } from "path";
6269
6686
  async function readSkillDescription(skillSourceDir) {
6270
6687
  const skillMdPath = resolve12(skillSourceDir, "SKILL.md");
6271
6688
  let raw;
6272
6689
  try {
6273
- raw = await readFile10(skillMdPath, "utf8");
6690
+ raw = await readFile9(skillMdPath, "utf8");
6274
6691
  } catch {
6275
6692
  return void 0;
6276
6693
  }
@@ -6300,11 +6717,11 @@ async function executeRollback(ledger) {
6300
6717
  if (entry.kind === "restore-file") {
6301
6718
  await atomicWriteFile(entry.path, entry.originalContent);
6302
6719
  } else if (entry.kind === "remove-file") {
6303
- await rm9(entry.path, { force: true });
6720
+ await rm8(entry.path, { force: true });
6304
6721
  } else if (entry.kind === "remove-dir") {
6305
- await rm9(entry.path, { recursive: true, force: true });
6722
+ await rm8(entry.path, { recursive: true, force: true });
6306
6723
  } else {
6307
- await rm9(entry.path, { recursive: true, force: true });
6724
+ await rm8(entry.path, { recursive: true, force: true });
6308
6725
  await safeRename(entry.backupPath, entry.path);
6309
6726
  }
6310
6727
  } catch (err) {
@@ -6316,20 +6733,20 @@ async function executeRollback(ledger) {
6316
6733
  return errors;
6317
6734
  }
6318
6735
  async function atomicWriteFile(filePath, content) {
6319
- await mkdir9(dirname9(filePath), { recursive: true });
6320
- const tmp = `${filePath}.${randomUUID7()}.tmp`;
6736
+ await mkdir8(dirname8(filePath), { recursive: true });
6737
+ const tmp = `${filePath}.${randomUUID6()}.tmp`;
6321
6738
  try {
6322
- await writeFile8(tmp, content, { encoding: "utf8", flag: "w" });
6739
+ await writeFile7(tmp, content, { encoding: "utf8", flag: "w" });
6323
6740
  await rename7(tmp, filePath);
6324
6741
  } catch (err) {
6325
- await rm9(tmp, { force: true }).catch(() => {
6742
+ await rm8(tmp, { force: true }).catch(() => {
6326
6743
  });
6327
6744
  throw err;
6328
6745
  }
6329
6746
  }
6330
6747
  async function getMcpMtimeMs(filePath) {
6331
6748
  try {
6332
- const s = await stat7(filePath);
6749
+ const s = await stat6(filePath);
6333
6750
  return s.mtimeMs;
6334
6751
  } catch {
6335
6752
  return null;
@@ -6562,13 +6979,13 @@ var CursorInstaller = class {
6562
6979
  if (info === "missing") continue;
6563
6980
  if (info === "skill-dir") {
6564
6981
  if (!dryRun) {
6565
- await rm9(filePath, { recursive: true, force: true });
6982
+ await rm8(filePath, { recursive: true, force: true });
6566
6983
  }
6567
6984
  removedFiles.push(filePath);
6568
6985
  continue;
6569
6986
  }
6570
6987
  if (info === "mdc-file") {
6571
- const content = await readFile11(filePath, "utf8").catch(() => "");
6988
+ const content = await readFile10(filePath, "utf8").catch(() => "");
6572
6989
  if (!hasRushAiMarker(content)) {
6573
6990
  warnings.push(
6574
6991
  `${filePath} \u5DF2\u88AB\u4FEE\u6539\uFF08marker \u4E22\u5931\uFF09\uFF0C\u8DF3\u8FC7\u4E0D\u52A8\u2014\u2014\u8BF7\u7528\u6237\u624B\u52A8\u5904\u7406`
@@ -6576,13 +6993,13 @@ var CursorInstaller = class {
6576
6993
  continue;
6577
6994
  }
6578
6995
  if (!dryRun) {
6579
- await rm9(filePath, { force: true });
6996
+ await rm8(filePath, { force: true });
6580
6997
  }
6581
6998
  removedFiles.push(filePath);
6582
6999
  continue;
6583
7000
  }
6584
7001
  if (!dryRun) {
6585
- await rm9(filePath, { force: true });
7002
+ await rm8(filePath, { force: true });
6586
7003
  }
6587
7004
  removedFiles.push(filePath);
6588
7005
  }
@@ -6677,7 +7094,7 @@ var CursorInstaller = class {
6677
7094
  `rule "${ruleName}" \u4E0E skill "${ruleName}" \u7684 bridge rule \u540C\u540D\uFF0Crule \u5185\u5BB9\u4F1A\u8986\u76D6 bridge rule`
6678
7095
  );
6679
7096
  }
6680
- const src = await readFile11(srcPath, "utf8");
7097
+ const src = await readFile10(srcPath, "utf8");
6681
7098
  const parsed = parseMarkdown(src);
6682
7099
  const content = renderMdc(parsed, {
6683
7100
  descriptionFallback: plugin.manifest.description ?? ruleName
@@ -6705,7 +7122,7 @@ var CursorInstaller = class {
6705
7122
  // -------------------------------------------------------------------------
6706
7123
  async writeSkillDir(args) {
6707
7124
  const preExisted = await dirExists2(args.destDir);
6708
- await mkdir9(dirname9(args.destDir), { recursive: true });
7125
+ await mkdir8(dirname8(args.destDir), { recursive: true });
6709
7126
  if (preExisted) {
6710
7127
  const backup = `${args.destDir}.${process.pid}.${Date.now()}.bak`;
6711
7128
  await safeRename(args.destDir, backup);
@@ -6722,7 +7139,7 @@ var CursorInstaller = class {
6722
7139
  async writeMdcFile(args) {
6723
7140
  const preExisted = await fileExists(args.path);
6724
7141
  if (preExisted) {
6725
- const original = await readFile11(args.path, "utf8");
7142
+ const original = await readFile10(args.path, "utf8");
6726
7143
  args.ledger.push({
6727
7144
  kind: "restore-file",
6728
7145
  path: args.path,
@@ -6736,7 +7153,7 @@ var CursorInstaller = class {
6736
7153
  async writeMcpMerge(args) {
6737
7154
  const preExisted = await fileExists(args.mcpPath);
6738
7155
  if (preExisted) {
6739
- const original = await readFile11(args.mcpPath, "utf8");
7156
+ const original = await readFile10(args.mcpPath, "utf8");
6740
7157
  args.ledger.push({
6741
7158
  kind: "restore-file",
6742
7159
  path: args.mcpPath,
@@ -6820,7 +7237,7 @@ function skippedResult() {
6820
7237
  }
6821
7238
  async function assertMdcWritable(path4, force) {
6822
7239
  if (!await fileExists(path4)) return;
6823
- const raw = await readFile11(path4, "utf8").catch(() => "");
7240
+ const raw = await readFile10(path4, "utf8").catch(() => "");
6824
7241
  if (hasRushAiMarker(raw)) return;
6825
7242
  if (force) return;
6826
7243
  throw new CursorInstallConflictError(
@@ -6854,7 +7271,7 @@ async function listRuleMdFiles(rulesDir) {
6854
7271
  }
6855
7272
  async function dirExists2(p) {
6856
7273
  try {
6857
- const s = await stat7(p);
7274
+ const s = await stat6(p);
6858
7275
  return s.isDirectory();
6859
7276
  } catch {
6860
7277
  return false;
@@ -6862,8 +7279,8 @@ async function dirExists2(p) {
6862
7279
  }
6863
7280
  async function fileExists(p) {
6864
7281
  try {
6865
- await access9(p, fsConstants9.F_OK);
6866
- const s = await stat7(p);
7282
+ await access8(p, fsConstants8.F_OK);
7283
+ const s = await stat6(p);
6867
7284
  return s.isFile();
6868
7285
  } catch {
6869
7286
  return false;
@@ -6876,7 +7293,7 @@ async function safeRename(from, to) {
6876
7293
  async function classifyArtifactPath(p) {
6877
7294
  let s;
6878
7295
  try {
6879
- s = await stat7(p);
7296
+ s = await stat6(p);
6880
7297
  } catch {
6881
7298
  return "missing";
6882
7299
  }
@@ -6889,12 +7306,12 @@ async function classifyArtifactPath(p) {
6889
7306
  }
6890
7307
 
6891
7308
  // src/migration/cleanup.ts
6892
- import { lstat as lstat3, rm as rm10, unlink as unlink2 } from "fs/promises";
7309
+ import { lstat as lstat3, rm as rm9, unlink as unlink2 } from "fs/promises";
6893
7310
  import { homedir as homedir9 } from "os";
6894
7311
  import { resolve as resolve15 } from "path";
6895
7312
 
6896
7313
  // src/migration/detect.ts
6897
- import { access as access10, lstat as lstat2, readFile as readFile12, readlink as readlink2, stat as stat8 } from "fs/promises";
7314
+ import { access as access9, lstat as lstat2, readFile as readFile11, readlink as readlink2, stat as stat7 } from "fs/promises";
6898
7315
  import { homedir as homedir8 } from "os";
6899
7316
  import { isAbsolute as isAbsolute4, resolve as resolve14 } from "path";
6900
7317
  var LEGACY_ASSET_MANIFEST_RELATIVE = ".rush/plugins/claude-code/asset-manifest.json";
@@ -6904,7 +7321,7 @@ var LEGACY_SKILL_SYMLINK_RELATIVE = ".claude/skills/rush-task";
6904
7321
  var CLAUDE_SETTINGS_JSON_RELATIVE = ".claude/settings.json";
6905
7322
  async function pathExists7(p) {
6906
7323
  try {
6907
- await access10(p);
7324
+ await access9(p);
6908
7325
  return true;
6909
7326
  } catch {
6910
7327
  return false;
@@ -6924,7 +7341,7 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
6924
7341
  async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
6925
7342
  try {
6926
7343
  if (!await pathExists7(settingsJsonPath)) return false;
6927
- const raw = await readFile12(settingsJsonPath, "utf8");
7344
+ const raw = await readFile11(settingsJsonPath, "utf8");
6928
7345
  let parsed;
6929
7346
  try {
6930
7347
  parsed = JSON.parse(raw);
@@ -6972,9 +7389,9 @@ async function detectLegacyVersion(opts = {}) {
6972
7389
  const home = opts.home ?? homedir8();
6973
7390
  const manifestPath = resolve14(home, LEGACY_ASSET_MANIFEST_RELATIVE);
6974
7391
  try {
6975
- const lst = await stat8(manifestPath);
7392
+ const lst = await stat7(manifestPath);
6976
7393
  if (!lst.isFile()) return "0.7.x";
6977
- const raw = await readFile12(manifestPath, "utf8");
7394
+ const raw = await readFile11(manifestPath, "utf8");
6978
7395
  const parsed = JSON.parse(raw);
6979
7396
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
6980
7397
  const v = parsed.version;
@@ -6990,7 +7407,7 @@ async function detectLegacyVersion(opts = {}) {
6990
7407
  async function cleanupLegacyDir(home) {
6991
7408
  const dir = resolve15(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
6992
7409
  try {
6993
- await rm10(dir, { recursive: true, force: true });
7410
+ await rm9(dir, { recursive: true, force: true });
6994
7411
  return { ok: true };
6995
7412
  } catch (err) {
6996
7413
  return {
@@ -7078,9 +7495,9 @@ function errorMessage(err) {
7078
7495
  }
7079
7496
 
7080
7497
  // src/migration/log.ts
7081
- import { appendFile as appendFile2, mkdir as mkdir10 } from "fs/promises";
7498
+ import { appendFile as appendFile2, mkdir as mkdir9 } from "fs/promises";
7082
7499
  import { homedir as homedir10 } from "os";
7083
- import { dirname as dirname10, resolve as resolve16 } from "path";
7500
+ import { dirname as dirname9, resolve as resolve16 } from "path";
7084
7501
  var MIGRATION_LOG_RELATIVE_PATH = ".rush/plugins/migration-failed.log";
7085
7502
  function resolveMigrationLogPath(opts = {}) {
7086
7503
  const home = opts.home ?? homedir10();
@@ -7101,14 +7518,14 @@ async function appendMigrationFailure(reason, opts = {}) {
7101
7518
  const payload = `${lines.join("\n")}
7102
7519
  `;
7103
7520
  try {
7104
- await mkdir10(dirname10(logPath), { recursive: true });
7521
+ await mkdir9(dirname9(logPath), { recursive: true });
7105
7522
  await appendFile2(logPath, payload, { encoding: "utf8" });
7106
7523
  } catch {
7107
7524
  }
7108
7525
  }
7109
7526
 
7110
7527
  // src/migration/migrate.ts
7111
- import { access as access13, readFile as readFile14 } from "fs/promises";
7528
+ import { access as access12, readFile as readFile13 } from "fs/promises";
7112
7529
  import { homedir as homedir11 } from "os";
7113
7530
 
7114
7531
  // src/plugins/resolver.ts
@@ -7120,9 +7537,9 @@ import {
7120
7537
  } from "path";
7121
7538
 
7122
7539
  // src/plugins/capabilities.ts
7123
- import { constants as fsConstants10 } from "fs";
7124
- import { access as access11, readdir as readdir4, stat as stat9 } from "fs/promises";
7125
- import { extname, resolve as resolve17 } from "path";
7540
+ import { constants as fsConstants9 } from "fs";
7541
+ import { access as access10, readdir as readdir4, stat as stat8 } from "fs/promises";
7542
+ import { extname as extname2, resolve as resolve17 } from "path";
7126
7543
  var CAPABILITY_ORDER = [
7127
7544
  "commands",
7128
7545
  "skills",
@@ -7155,23 +7572,22 @@ async function hasCommands(sourceDir) {
7155
7572
  }
7156
7573
  async function hasSkills(sourceDir) {
7157
7574
  const skillsDir = resolve17(sourceDir, "skills");
7158
- if (!await dirExists3(skillsDir)) {
7159
- return false;
7160
- }
7161
- let entries;
7162
- try {
7163
- entries = await readdir4(skillsDir, { withFileTypes: true });
7164
- } catch {
7165
- return false;
7166
- }
7167
- for (const entry of entries) {
7168
- if (!entry.isDirectory()) continue;
7169
- const skillMdPath = resolve17(skillsDir, entry.name, "SKILL.md");
7170
- if (await fileExists2(skillMdPath)) {
7171
- return true;
7575
+ if (await dirExists3(skillsDir)) {
7576
+ let entries;
7577
+ try {
7578
+ entries = await readdir4(skillsDir, { withFileTypes: true });
7579
+ } catch {
7580
+ return false;
7581
+ }
7582
+ for (const entry of entries) {
7583
+ if (!entry.isDirectory()) continue;
7584
+ const skillMdPath = resolve17(skillsDir, entry.name, "SKILL.md");
7585
+ if (await fileExists2(skillMdPath)) {
7586
+ return true;
7587
+ }
7172
7588
  }
7173
7589
  }
7174
- return false;
7590
+ return hasClaudeStyleSkills(sourceDir);
7175
7591
  }
7176
7592
  async function hasRules(sourceDir) {
7177
7593
  return dirHasFileWithExt(resolve17(sourceDir, "rules"), ".md");
@@ -7189,7 +7605,7 @@ async function hasHooks(sourceDir) {
7189
7605
  }
7190
7606
  for (const entry of entries) {
7191
7607
  if (!entry.isFile()) continue;
7192
- if (HOOK_EXTENSIONS.has(extname(entry.name).toLowerCase())) {
7608
+ if (HOOK_EXTENSIONS.has(extname2(entry.name).toLowerCase())) {
7193
7609
  return true;
7194
7610
  }
7195
7611
  }
@@ -7215,12 +7631,38 @@ async function dirHasFileWithExt(dirPath, ext) {
7215
7631
  }
7216
7632
  const target = ext.toLowerCase();
7217
7633
  return entries.some(
7218
- (e) => e.isFile() && extname(e.name).toLowerCase() === target
7634
+ (e) => e.isFile() && extname2(e.name).toLowerCase() === target
7219
7635
  );
7220
7636
  }
7637
+ async function hasClaudeStyleSkills(sourceDir) {
7638
+ const dotSkillsDir = resolve17(sourceDir, ".skills");
7639
+ if (!await dirExists3(dotSkillsDir)) {
7640
+ return false;
7641
+ }
7642
+ async function walk2(dirPath) {
7643
+ let entries;
7644
+ try {
7645
+ entries = await readdir4(dirPath, { withFileTypes: true });
7646
+ } catch {
7647
+ return false;
7648
+ }
7649
+ for (const entry of entries) {
7650
+ const abs = resolve17(dirPath, entry.name);
7651
+ if (entry.isDirectory()) {
7652
+ if (await walk2(abs)) return true;
7653
+ continue;
7654
+ }
7655
+ if (entry.isFile() && extname2(entry.name).toLowerCase() === ".md") {
7656
+ return true;
7657
+ }
7658
+ }
7659
+ return false;
7660
+ }
7661
+ return walk2(dotSkillsDir);
7662
+ }
7221
7663
  async function dirExists3(p) {
7222
7664
  try {
7223
- const s = await stat9(p);
7665
+ const s = await stat8(p);
7224
7666
  return s.isDirectory();
7225
7667
  } catch {
7226
7668
  return false;
@@ -7228,8 +7670,8 @@ async function dirExists3(p) {
7228
7670
  }
7229
7671
  async function fileExists2(p) {
7230
7672
  try {
7231
- await access11(p, fsConstants10.R_OK);
7232
- const s = await stat9(p);
7673
+ await access10(p, fsConstants9.R_OK);
7674
+ const s = await stat8(p);
7233
7675
  return s.isFile();
7234
7676
  } catch {
7235
7677
  return false;
@@ -7298,18 +7740,18 @@ var InvalidPluginVersionError = class extends PluginResolverError {
7298
7740
  };
7299
7741
 
7300
7742
  // src/plugins/manifest.ts
7301
- import { constants as fsConstants11 } from "fs";
7302
- import { access as access12, readFile as readFile13 } from "fs/promises";
7743
+ import { constants as fsConstants10 } from "fs";
7744
+ import { access as access11, readFile as readFile12 } from "fs/promises";
7303
7745
  import { resolve as resolve18 } from "path";
7304
7746
  var PLUGIN_MANIFEST_RELATIVE_PATH = ".claude-plugin/plugin.json";
7305
7747
  async function readPluginManifest(pluginName, sourceDir) {
7306
7748
  const manifestPath = resolve18(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
7307
7749
  try {
7308
- await access12(manifestPath, fsConstants11.R_OK);
7750
+ await access11(manifestPath, fsConstants10.R_OK);
7309
7751
  } catch {
7310
7752
  throw new PluginManifestNotFoundError(pluginName, manifestPath);
7311
7753
  }
7312
- const raw = await readFile13(manifestPath, "utf8");
7754
+ const raw = await readFile12(manifestPath, "utf8");
7313
7755
  return parsePluginManifest(pluginName, raw, manifestPath);
7314
7756
  }
7315
7757
  function parsePluginManifest(pluginName, raw, sourcePathForError) {
@@ -7705,7 +8147,7 @@ function isTruthyEnvValue(value) {
7705
8147
  }
7706
8148
  async function verifyNewFormat(manifestPath, expectedName, _expectedVersion) {
7707
8149
  try {
7708
- await access13(manifestPath);
8150
+ await access12(manifestPath);
7709
8151
  } catch (err) {
7710
8152
  return {
7711
8153
  ok: false,
@@ -7714,7 +8156,7 @@ async function verifyNewFormat(manifestPath, expectedName, _expectedVersion) {
7714
8156
  }
7715
8157
  let raw;
7716
8158
  try {
7717
- raw = await readFile14(manifestPath, "utf8");
8159
+ raw = await readFile13(manifestPath, "utf8");
7718
8160
  } catch (err) {
7719
8161
  return {
7720
8162
  ok: false,
@@ -8827,7 +9269,7 @@ function registerPluginCommand(program) {
8827
9269
 
8828
9270
  // src/commands/skill/index.ts
8829
9271
  import { existsSync as existsSync11, readdirSync, readFileSync as readFileSync7 } from "fs";
8830
- import { basename as basename2, resolve as resolve19 } from "path";
9272
+ import { basename as basename3, resolve as resolve19 } from "path";
8831
9273
  var SKILL_DESCRIPTION = "Print agent usage skills (hand-off, agent-shelf, ...) as raw markdown.";
8832
9274
  var SKILL_HELP_AFTER = `
8833
9275
  Examples:
@@ -8856,7 +9298,7 @@ function resolveSkillsDir() {
8856
9298
  return null;
8857
9299
  }
8858
9300
  function listSkills(dir) {
8859
- return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename2(f, ".md")).sort();
9301
+ return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename3(f, ".md")).sort();
8860
9302
  }
8861
9303
  function registerSkillCommand(program) {
8862
9304
  program.command("skill [name]").description(SKILL_DESCRIPTION).addHelpText("after", SKILL_HELP_AFTER).option("--list", "List available skill names and exit").action((name, opts) => {
@@ -8887,7 +9329,7 @@ function registerSkillCommand(program) {
8887
9329
 
8888
9330
  // src/commands/task/index.ts
8889
9331
  import { createWriteStream } from "fs";
8890
- import { mkdir as mkdir11, readFile as readFile15, stat as stat10 } from "fs/promises";
9332
+ import { mkdir as mkdir10, readFile as readFile14, stat as stat9 } from "fs/promises";
8891
9333
  import path3 from "path";
8892
9334
  import { Readable } from "stream";
8893
9335
  import { pipeline } from "stream/promises";
@@ -10419,7 +10861,7 @@ async function downloadFile(file2, destPath, client) {
10419
10861
  if (!response.body) {
10420
10862
  throw new Error("No response body");
10421
10863
  }
10422
- await mkdir11(path3.dirname(destPath), { recursive: true });
10864
+ await mkdir10(path3.dirname(destPath), { recursive: true });
10423
10865
  const nodeStream = Readable.fromWeb(
10424
10866
  response.body
10425
10867
  );
@@ -10490,7 +10932,7 @@ function registerTaskCommand(program) {
10490
10932
  }
10491
10933
  );
10492
10934
  task.command("send").description("Send a message to an existing task").argument("<id>", "Task ID (project ID)").option("-p, --prompt <text>", "Message to send").option("--skills <skills>", "Comma-separated skills to add", commaSplit).option("--mcp <servers>", "Comma-separated MCP servers to add", commaSplit).option(
10493
- "-f, --file <path>",
10935
+ "--file <path>",
10494
10936
  "Attach a local file as context for the agent (repeatable)",
10495
10937
  collectFile,
10496
10938
  []
@@ -10959,7 +11401,7 @@ async function uploadFileForTask(client, localPath) {
10959
11401
  const resolved = path3.resolve(localPath);
10960
11402
  let fileStat;
10961
11403
  try {
10962
- fileStat = await stat10(resolved);
11404
+ fileStat = await stat9(resolved);
10963
11405
  } catch {
10964
11406
  throw new RushError(`File not found: ${localPath}`);
10965
11407
  }
@@ -10973,7 +11415,7 @@ async function uploadFileForTask(client, localPath) {
10973
11415
  }
10974
11416
  const filename = path3.basename(resolved);
10975
11417
  const mediaType = guessMediaType(resolved);
10976
- const buf = await readFile15(resolved);
11418
+ const buf = await readFile14(resolved);
10977
11419
  const blob = new File([buf], filename, { type: mediaType });
10978
11420
  const formData = new FormData();
10979
11421
  formData.append("file", blob);
@@ -11025,9 +11467,9 @@ function registerCommands(program) {
11025
11467
  }
11026
11468
 
11027
11469
  // src/util/update-check.ts
11028
- import { mkdir as mkdir12, readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
11470
+ import { mkdir as mkdir11, readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
11029
11471
  import { homedir as homedir12 } from "os";
11030
- import { dirname as dirname11, join as join10 } from "path";
11472
+ import { dirname as dirname10, join as join10 } from "path";
11031
11473
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
11032
11474
  var FETCH_TIMEOUT_MS = 3e3;
11033
11475
  var REGISTRY_URL = "https://registry.npmjs.org/rush-ai/latest";
@@ -11050,8 +11492,8 @@ function isNewerVersion(current, latest) {
11050
11492
  }
11051
11493
  async function writeLastCheck(checkFile) {
11052
11494
  try {
11053
- await mkdir12(dirname11(checkFile), { recursive: true });
11054
- await writeFile9(checkFile, JSON.stringify({ lastCheck: Date.now() }));
11495
+ await mkdir11(dirname10(checkFile), { recursive: true });
11496
+ await writeFile8(checkFile, JSON.stringify({ lastCheck: Date.now() }));
11055
11497
  } catch {
11056
11498
  }
11057
11499
  }
@@ -11062,7 +11504,7 @@ async function checkForUpdate(currentVersion) {
11062
11504
  if (process.env.CI) return;
11063
11505
  let lastCheck = 0;
11064
11506
  try {
11065
- const data = JSON.parse(await readFile16(checkFile, "utf-8"));
11507
+ const data = JSON.parse(await readFile15(checkFile, "utf-8"));
11066
11508
  lastCheck = data.lastCheck ?? 0;
11067
11509
  } catch {
11068
11510
  }