rush-ai 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -9
- package/dist/{_codex-content-loader-Q7KBWZSB.js → _codex-content-loader-WAPA56U3.js} +2 -2
- package/dist/{chunk-X45FKY3L.js → chunk-22YQT6XF.js} +18 -7
- package/dist/chunk-22YQT6XF.js.map +1 -0
- package/dist/chunk-C7KJUHEF.js +830 -0
- package/dist/chunk-C7KJUHEF.js.map +1 -0
- package/dist/{chunk-RLKEUPBP.js → chunk-GPOA7SAG.js} +18 -3
- package/dist/chunk-GPOA7SAG.js.map +1 -0
- package/dist/index.js +1521 -346
- package/dist/index.js.map +1 -1
- package/dist/{mcp-UZYID3GG.js → mcp-TROKQRBF.js} +2 -2
- package/dist/{mirror-IHLOSPAS.js → mirror-SQ6GNVP5.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-2AICQRQP.js +0 -447
- package/dist/chunk-2AICQRQP.js.map +0 -1
- package/dist/chunk-RLKEUPBP.js.map +0 -1
- package/dist/chunk-X45FKY3L.js.map +0 -1
- /package/dist/{_codex-content-loader-Q7KBWZSB.js.map → _codex-content-loader-WAPA56U3.js.map} +0 -0
- /package/dist/{mcp-UZYID3GG.js.map → mcp-TROKQRBF.js.map} +0 -0
- /package/dist/{mirror-IHLOSPAS.js.map → mirror-SQ6GNVP5.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -25,14 +25,14 @@ import {
|
|
|
25
25
|
syncCodexMarketplaceMirror,
|
|
26
26
|
writeCodexConfig,
|
|
27
27
|
writeCodexMarketplacePluginMirror
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-GPOA7SAG.js";
|
|
29
29
|
import {
|
|
30
30
|
SkillAuthError,
|
|
31
31
|
fetchRushMarketplace,
|
|
32
32
|
fetchRushPlugin,
|
|
33
33
|
materializeRushPlugin,
|
|
34
34
|
pickContentLoader
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-C7KJUHEF.js";
|
|
36
36
|
import {
|
|
37
37
|
consumeSSEStreamWithReconnect
|
|
38
38
|
} from "./chunk-MG4HY2PD.js";
|
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
defaultRushBinaryResolver,
|
|
82
82
|
normalizeClaudeMcpServers,
|
|
83
83
|
readExternalMcpServers
|
|
84
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-22YQT6XF.js";
|
|
85
85
|
|
|
86
86
|
// src/index.ts
|
|
87
87
|
import chalk7 from "chalk";
|
|
@@ -446,7 +446,7 @@ function getApiBaseUrl() {
|
|
|
446
446
|
async function loginViaBrowser(jsonMode) {
|
|
447
447
|
const baseUrl = getApiBaseUrl();
|
|
448
448
|
const state = randomBytes(16).toString("hex");
|
|
449
|
-
return new Promise((
|
|
449
|
+
return new Promise((resolve25, reject) => {
|
|
450
450
|
let authSaved = false;
|
|
451
451
|
const rejectLogin = (error) => {
|
|
452
452
|
clearAuthConfig();
|
|
@@ -549,7 +549,7 @@ async function loginViaBrowser(jsonMode) {
|
|
|
549
549
|
);
|
|
550
550
|
}
|
|
551
551
|
}
|
|
552
|
-
|
|
552
|
+
resolve25();
|
|
553
553
|
} catch (err) {
|
|
554
554
|
server.close();
|
|
555
555
|
const authError = err instanceof AuthError ? err : new AuthError(
|
|
@@ -3357,8 +3357,8 @@ async function writeFileAtomic(filePath, content) {
|
|
|
3357
3357
|
const tmp = `${filePath}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
3358
3358
|
try {
|
|
3359
3359
|
await writeFile2(tmp, content, { encoding: "utf8", flag: "w" });
|
|
3360
|
-
const { rename:
|
|
3361
|
-
await
|
|
3360
|
+
const { rename: rename8 } = await import("fs/promises");
|
|
3361
|
+
await rename8(tmp, filePath);
|
|
3362
3362
|
} catch (err) {
|
|
3363
3363
|
await rm(tmp, { force: true }).catch(() => {
|
|
3364
3364
|
});
|
|
@@ -4170,12 +4170,41 @@ var MarketplaceCache = class {
|
|
|
4170
4170
|
const data = await fetchRushMarketplace(source, { token });
|
|
4171
4171
|
const manifest = {
|
|
4172
4172
|
name: data.name || "rush",
|
|
4173
|
-
plugins: data.plugins.map((p) =>
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4173
|
+
plugins: data.plugins.map((p) => {
|
|
4174
|
+
const entry = {
|
|
4175
|
+
name: p.name,
|
|
4176
|
+
version: p.version,
|
|
4177
|
+
description: p.description,
|
|
4178
|
+
source: `./plugins/${p.name}`
|
|
4179
|
+
};
|
|
4180
|
+
const cat = p.category;
|
|
4181
|
+
if (typeof cat === "string" && cat.length > 0) {
|
|
4182
|
+
entry.category = cat;
|
|
4183
|
+
}
|
|
4184
|
+
const apiAuthor = p.author;
|
|
4185
|
+
if (apiAuthor && typeof apiAuthor === "object") {
|
|
4186
|
+
entry.author = { ...apiAuthor };
|
|
4187
|
+
}
|
|
4188
|
+
const apiHomepage = p.homepage;
|
|
4189
|
+
let homepageOut;
|
|
4190
|
+
if (typeof apiHomepage === "string" && apiHomepage.length > 0) {
|
|
4191
|
+
homepageOut = apiHomepage;
|
|
4192
|
+
} else {
|
|
4193
|
+
const apiSource = p.source;
|
|
4194
|
+
if (apiSource && typeof apiSource === "object") {
|
|
4195
|
+
const sUrl = apiSource.url;
|
|
4196
|
+
if (typeof sUrl === "string" && /^https?:\/\//i.test(sUrl)) {
|
|
4197
|
+
homepageOut = sUrl.replace(/\.git$/, "");
|
|
4198
|
+
}
|
|
4199
|
+
} else if (typeof apiSource === "string" && /^https?:\/\//i.test(apiSource)) {
|
|
4200
|
+
homepageOut = apiSource;
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
if (homepageOut) {
|
|
4204
|
+
entry.homepage = homepageOut;
|
|
4205
|
+
}
|
|
4206
|
+
return entry;
|
|
4207
|
+
})
|
|
4179
4208
|
};
|
|
4180
4209
|
const resolvedName = name ?? defaultNameFromSource(source);
|
|
4181
4210
|
const rootDir = this.pathFor(resolvedName);
|
|
@@ -5448,107 +5477,1113 @@ function registerMcpCommand(program) {
|
|
|
5448
5477
|
}
|
|
5449
5478
|
});
|
|
5450
5479
|
}
|
|
5451
|
-
|
|
5452
|
-
// src/commands/playbook/index.ts
|
|
5453
|
-
import { existsSync as existsSync11, readdirSync, readFileSync as readFileSync7 } from "fs";
|
|
5454
|
-
import { basename as basename3, resolve as resolve9 } from "path";
|
|
5455
|
-
var PLAYBOOK_DESCRIPTION = "Print agent usage playbooks (hand-off, agent-shelf, ...) as raw markdown.";
|
|
5456
|
-
var PLAYBOOK_HELP_AFTER = `
|
|
5457
|
-
Examples:
|
|
5458
|
-
$ npx rush-ai playbook Print the playbook index
|
|
5459
|
-
$ npx rush-ai playbook hand-off Print the hand-off playbook
|
|
5460
|
-
$ npx rush-ai playbook agent-shelf Print the agent-shelf playbook
|
|
5461
|
-
$ npx rush-ai playbook --list List available playbooks
|
|
5462
|
-
|
|
5463
|
-
Designed to be consumed by AI agents inside IDEs (Cursor, Claude Code,
|
|
5464
|
-
Codex, etc). Output is raw markdown on stdout \u2014 pipe it, grep it, feed it
|
|
5465
|
-
to your own context.
|
|
5466
|
-
`;
|
|
5467
|
-
function resolvePlaybooksDir() {
|
|
5468
|
-
const baseDir = import.meta.dirname ?? __dirname;
|
|
5469
|
-
if (!baseDir) return null;
|
|
5470
|
-
const candidates = [
|
|
5471
|
-
resolve9(baseDir, "skills"),
|
|
5472
|
-
resolve9(baseDir, "..", "skills"),
|
|
5473
|
-
resolve9(baseDir, "..", "..", "skills"),
|
|
5474
|
-
resolve9(baseDir, "..", "..", "..", "skills"),
|
|
5475
|
-
resolve9(baseDir, "..", "..", "..", "..", "skills")
|
|
5476
|
-
];
|
|
5477
|
-
for (const p of candidates) {
|
|
5478
|
-
if (existsSync11(resolve9(p, ".rush-playbooks"))) return p;
|
|
5480
|
+
|
|
5481
|
+
// src/commands/playbook/index.ts
|
|
5482
|
+
import { existsSync as existsSync11, readdirSync, readFileSync as readFileSync7 } from "fs";
|
|
5483
|
+
import { basename as basename3, resolve as resolve9 } from "path";
|
|
5484
|
+
var PLAYBOOK_DESCRIPTION = "Print agent usage playbooks (hand-off, agent-shelf, ...) as raw markdown.";
|
|
5485
|
+
var PLAYBOOK_HELP_AFTER = `
|
|
5486
|
+
Examples:
|
|
5487
|
+
$ npx rush-ai playbook Print the playbook index
|
|
5488
|
+
$ npx rush-ai playbook hand-off Print the hand-off playbook
|
|
5489
|
+
$ npx rush-ai playbook agent-shelf Print the agent-shelf playbook
|
|
5490
|
+
$ npx rush-ai playbook --list List available playbooks
|
|
5491
|
+
|
|
5492
|
+
Designed to be consumed by AI agents inside IDEs (Cursor, Claude Code,
|
|
5493
|
+
Codex, etc). Output is raw markdown on stdout \u2014 pipe it, grep it, feed it
|
|
5494
|
+
to your own context.
|
|
5495
|
+
`;
|
|
5496
|
+
function resolvePlaybooksDir() {
|
|
5497
|
+
const baseDir = import.meta.dirname ?? __dirname;
|
|
5498
|
+
if (!baseDir) return null;
|
|
5499
|
+
const candidates = [
|
|
5500
|
+
resolve9(baseDir, "skills"),
|
|
5501
|
+
resolve9(baseDir, "..", "skills"),
|
|
5502
|
+
resolve9(baseDir, "..", "..", "skills"),
|
|
5503
|
+
resolve9(baseDir, "..", "..", "..", "skills"),
|
|
5504
|
+
resolve9(baseDir, "..", "..", "..", "..", "skills")
|
|
5505
|
+
];
|
|
5506
|
+
for (const p of candidates) {
|
|
5507
|
+
if (existsSync11(resolve9(p, ".rush-playbooks"))) return p;
|
|
5508
|
+
}
|
|
5509
|
+
return null;
|
|
5510
|
+
}
|
|
5511
|
+
function listPlaybooks(dir) {
|
|
5512
|
+
return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename3(f, ".md")).sort();
|
|
5513
|
+
}
|
|
5514
|
+
function getAvailablePlaybooks() {
|
|
5515
|
+
const dir = resolvePlaybooksDir();
|
|
5516
|
+
return dir ? listPlaybooks(dir) : [];
|
|
5517
|
+
}
|
|
5518
|
+
function isPlaybookName(name) {
|
|
5519
|
+
if (!name) return false;
|
|
5520
|
+
return getAvailablePlaybooks().includes(name);
|
|
5521
|
+
}
|
|
5522
|
+
function printPlaybook(name, opts = {}) {
|
|
5523
|
+
const dir = resolvePlaybooksDir();
|
|
5524
|
+
if (!dir) {
|
|
5525
|
+
output.error(
|
|
5526
|
+
"Could not locate the skills/ directory. This is a packaging bug \u2014 please file an issue."
|
|
5527
|
+
);
|
|
5528
|
+
process.exit(2);
|
|
5529
|
+
return;
|
|
5530
|
+
}
|
|
5531
|
+
const available = listPlaybooks(dir);
|
|
5532
|
+
if (opts.list) {
|
|
5533
|
+
for (const s of available) output.log(s);
|
|
5534
|
+
return;
|
|
5535
|
+
}
|
|
5536
|
+
const target = name ?? "README";
|
|
5537
|
+
const file2 = resolve9(dir, `${target}.md`);
|
|
5538
|
+
if (!existsSync11(file2)) {
|
|
5539
|
+
output.error(`Unknown playbook "${target}".`);
|
|
5540
|
+
output.dim(`Available: ${available.join(", ") || "(none)"}`);
|
|
5541
|
+
process.exit(1);
|
|
5542
|
+
return;
|
|
5543
|
+
}
|
|
5544
|
+
process.stdout.write(readFileSync7(file2, "utf-8"));
|
|
5545
|
+
}
|
|
5546
|
+
function registerPlaybookCommand(program) {
|
|
5547
|
+
program.command("playbook [name]").description(PLAYBOOK_DESCRIPTION).addHelpText("after", PLAYBOOK_HELP_AFTER).option("--list", "List available playbook names and exit").action((name, opts) => {
|
|
5548
|
+
printPlaybook(name, opts);
|
|
5549
|
+
});
|
|
5550
|
+
}
|
|
5551
|
+
|
|
5552
|
+
// src/commands/plugin/install.ts
|
|
5553
|
+
import { rm as rm12 } from "fs/promises";
|
|
5554
|
+
import { homedir as homedir18 } from "os";
|
|
5555
|
+
import { resolve as resolve22 } from "path";
|
|
5556
|
+
|
|
5557
|
+
// src/installers/claude-3p/installer.ts
|
|
5558
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5559
|
+
import {
|
|
5560
|
+
cp as cp2,
|
|
5561
|
+
mkdir as mkdir5,
|
|
5562
|
+
readdir as readdir4,
|
|
5563
|
+
readFile as readFile6,
|
|
5564
|
+
rename as rename3,
|
|
5565
|
+
rm as rm6,
|
|
5566
|
+
writeFile as writeFile5
|
|
5567
|
+
} from "fs/promises";
|
|
5568
|
+
import { homedir as homedir10 } from "os";
|
|
5569
|
+
import { dirname as dirname5, join as join9, resolve as resolve11 } from "path";
|
|
5570
|
+
|
|
5571
|
+
// src/installers/claude-3p/paths.ts
|
|
5572
|
+
import { readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
5573
|
+
import { resolve as resolve10 } from "path";
|
|
5574
|
+
var CLAUDE_3P_APP_DIR = "Claude-3p";
|
|
5575
|
+
var LOCAL_AGENT_SESSIONS_DIR = "local-agent-mode-sessions";
|
|
5576
|
+
var COWORK_PLUGINS_DIR = "cowork_plugins";
|
|
5577
|
+
var PERSONAL_ORG_UUID = "00000000-0000-4000-8000-000000000001";
|
|
5578
|
+
var PLUGIN_MANIFEST_RELATIVE = ".claude-plugin/plugin.json";
|
|
5579
|
+
var MARKETPLACE_MANIFEST_RELATIVE = ".claude-plugin/marketplace.json";
|
|
5580
|
+
var UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
5581
|
+
var ClaudeCpPaths = class {
|
|
5582
|
+
constructor(home) {
|
|
5583
|
+
this.home = home;
|
|
5584
|
+
}
|
|
5585
|
+
/** `<home>/Library/Application Support/Claude-3p/` */
|
|
5586
|
+
get appDir() {
|
|
5587
|
+
return resolve10(
|
|
5588
|
+
this.home,
|
|
5589
|
+
"Library",
|
|
5590
|
+
"Application Support",
|
|
5591
|
+
CLAUDE_3P_APP_DIR
|
|
5592
|
+
);
|
|
5593
|
+
}
|
|
5594
|
+
/** `<appDir>/local-agent-mode-sessions/` */
|
|
5595
|
+
get sessionsDir() {
|
|
5596
|
+
return resolve10(this.appDir, LOCAL_AGENT_SESSIONS_DIR);
|
|
5597
|
+
}
|
|
5598
|
+
/** `<sessionsDir>/<account-uuid>/` */
|
|
5599
|
+
accountDir(accountUuid) {
|
|
5600
|
+
return resolve10(this.sessionsDir, accountUuid);
|
|
5601
|
+
}
|
|
5602
|
+
/** `<sessionsDir>/<account-uuid>/<org-uuid>/` */
|
|
5603
|
+
orgDir(accountUuid, orgUuid) {
|
|
5604
|
+
return resolve10(this.accountDir(accountUuid), orgUuid);
|
|
5605
|
+
}
|
|
5606
|
+
/** `<orgDir>/cowork_account_settings.json` */
|
|
5607
|
+
accountSettingsJson(accountUuid, orgUuid) {
|
|
5608
|
+
return resolve10(
|
|
5609
|
+
this.orgDir(accountUuid, orgUuid),
|
|
5610
|
+
"cowork_account_settings.json"
|
|
5611
|
+
);
|
|
5612
|
+
}
|
|
5613
|
+
/** `<orgDir>/cowork_settings.json` */
|
|
5614
|
+
coworkSettingsJson(accountUuid, orgUuid) {
|
|
5615
|
+
return resolve10(this.orgDir(accountUuid, orgUuid), "cowork_settings.json");
|
|
5616
|
+
}
|
|
5617
|
+
/** `<orgDir>/cowork_plugins/` */
|
|
5618
|
+
coworkPluginsDir(accountUuid, orgUuid) {
|
|
5619
|
+
return resolve10(this.orgDir(accountUuid, orgUuid), COWORK_PLUGINS_DIR);
|
|
5620
|
+
}
|
|
5621
|
+
/** `<cowork_plugins>/installed_plugins.json` */
|
|
5622
|
+
installedPluginsJson(accountUuid, orgUuid) {
|
|
5623
|
+
return resolve10(
|
|
5624
|
+
this.coworkPluginsDir(accountUuid, orgUuid),
|
|
5625
|
+
"installed_plugins.json"
|
|
5626
|
+
);
|
|
5627
|
+
}
|
|
5628
|
+
/** `<cowork_plugins>/known_marketplaces.json` */
|
|
5629
|
+
knownMarketplacesJson(accountUuid, orgUuid) {
|
|
5630
|
+
return resolve10(
|
|
5631
|
+
this.coworkPluginsDir(accountUuid, orgUuid),
|
|
5632
|
+
"known_marketplaces.json"
|
|
5633
|
+
);
|
|
5634
|
+
}
|
|
5635
|
+
/** `<cowork_plugins>/org_uninstalled.json` */
|
|
5636
|
+
orgUninstalledJson(accountUuid, orgUuid) {
|
|
5637
|
+
return resolve10(
|
|
5638
|
+
this.coworkPluginsDir(accountUuid, orgUuid),
|
|
5639
|
+
"org_uninstalled.json"
|
|
5640
|
+
);
|
|
5641
|
+
}
|
|
5642
|
+
/** `<cowork_plugins>/marketplaces/` */
|
|
5643
|
+
marketplacesRootDir(accountUuid, orgUuid) {
|
|
5644
|
+
return resolve10(this.coworkPluginsDir(accountUuid, orgUuid), "marketplaces");
|
|
5645
|
+
}
|
|
5646
|
+
/** `<cowork_plugins>/marketplaces/<mkt>/` */
|
|
5647
|
+
marketplaceDir(accountUuid, orgUuid, marketplace) {
|
|
5648
|
+
return resolve10(this.marketplacesRootDir(accountUuid, orgUuid), marketplace);
|
|
5649
|
+
}
|
|
5650
|
+
/** `<cowork_plugins>/marketplaces/<mkt>/.claude-plugin/marketplace.json` */
|
|
5651
|
+
marketplaceManifestPath(accountUuid, orgUuid, marketplace) {
|
|
5652
|
+
return resolve10(
|
|
5653
|
+
this.marketplaceDir(accountUuid, orgUuid, marketplace),
|
|
5654
|
+
MARKETPLACE_MANIFEST_RELATIVE
|
|
5655
|
+
);
|
|
5656
|
+
}
|
|
5657
|
+
/** `<cowork_plugins>/marketplaces/<mkt>/<plugin>/` */
|
|
5658
|
+
pluginDir(accountUuid, orgUuid, ref) {
|
|
5659
|
+
return resolve10(
|
|
5660
|
+
this.marketplaceDir(accountUuid, orgUuid, ref.marketplace),
|
|
5661
|
+
ref.name
|
|
5662
|
+
);
|
|
5663
|
+
}
|
|
5664
|
+
/** `<pluginDir>/.claude-plugin/plugin.json` */
|
|
5665
|
+
pluginManifestPath(accountUuid, orgUuid, ref) {
|
|
5666
|
+
return resolve10(
|
|
5667
|
+
this.pluginDir(accountUuid, orgUuid, ref),
|
|
5668
|
+
PLUGIN_MANIFEST_RELATIVE
|
|
5669
|
+
);
|
|
5670
|
+
}
|
|
5671
|
+
/** `<pluginDir>/.mcp.json` */
|
|
5672
|
+
pluginMcpJsonPath(accountUuid, orgUuid, ref) {
|
|
5673
|
+
return resolve10(this.pluginDir(accountUuid, orgUuid, ref), ".mcp.json");
|
|
5674
|
+
}
|
|
5675
|
+
/** `<pluginDir>/<capability>/` */
|
|
5676
|
+
capabilityDir(accountUuid, orgUuid, ref, capability) {
|
|
5677
|
+
return resolve10(this.pluginDir(accountUuid, orgUuid, ref), capability);
|
|
5678
|
+
}
|
|
5679
|
+
};
|
|
5680
|
+
function pluginKey(ref) {
|
|
5681
|
+
return `${ref.name}@${ref.marketplace}`;
|
|
5682
|
+
}
|
|
5683
|
+
async function resolveClaudeCpAccount(paths, options = {}) {
|
|
5684
|
+
const orgUuid = options.orgUuid ?? PERSONAL_ORG_UUID;
|
|
5685
|
+
if (options.accountUuid !== void 0) {
|
|
5686
|
+
if (!UUID_V4_RE.test(options.accountUuid)) return null;
|
|
5687
|
+
const settingsPath = paths.accountSettingsJson(
|
|
5688
|
+
options.accountUuid,
|
|
5689
|
+
orgUuid
|
|
5690
|
+
);
|
|
5691
|
+
if (!await pathExists(settingsPath)) return null;
|
|
5692
|
+
return { accountUuid: options.accountUuid, orgUuid, warnings: [] };
|
|
5693
|
+
}
|
|
5694
|
+
let entries;
|
|
5695
|
+
try {
|
|
5696
|
+
entries = await readdir3(paths.sessionsDir, { withFileTypes: true });
|
|
5697
|
+
} catch {
|
|
5698
|
+
return null;
|
|
5699
|
+
}
|
|
5700
|
+
const accountDirs = entries.filter(
|
|
5701
|
+
(entry) => entry.isDirectory() && UUID_V4_RE.test(entry.name)
|
|
5702
|
+
);
|
|
5703
|
+
if (accountDirs.length === 0) return null;
|
|
5704
|
+
const candidates = [];
|
|
5705
|
+
for (const entry of accountDirs) {
|
|
5706
|
+
const settingsPath = paths.accountSettingsJson(entry.name, orgUuid);
|
|
5707
|
+
if (!await pathExists(settingsPath)) continue;
|
|
5708
|
+
const stats = await stat3(paths.accountDir(entry.name)).catch(() => null);
|
|
5709
|
+
candidates.push({
|
|
5710
|
+
accountUuid: entry.name,
|
|
5711
|
+
mtimeMs: stats?.mtimeMs ?? 0
|
|
5712
|
+
});
|
|
5713
|
+
}
|
|
5714
|
+
if (candidates.length === 0) return null;
|
|
5715
|
+
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
5716
|
+
const pick = candidates[0];
|
|
5717
|
+
if (!pick) return null;
|
|
5718
|
+
const warnings = candidates.length > 1 ? [
|
|
5719
|
+
`Multiple Claude-3p accounts found; selected most recent account ${pick.accountUuid}. Pass accountUuid to override.`
|
|
5720
|
+
] : [];
|
|
5721
|
+
return { accountUuid: pick.accountUuid, orgUuid, warnings };
|
|
5722
|
+
}
|
|
5723
|
+
|
|
5724
|
+
// src/installers/claude-3p/installer.ts
|
|
5725
|
+
var ClaudeCpInstaller = class {
|
|
5726
|
+
target = "claude-3p";
|
|
5727
|
+
paths;
|
|
5728
|
+
accountOptions;
|
|
5729
|
+
rushBinaryResolver;
|
|
5730
|
+
now;
|
|
5731
|
+
marketplaceSource;
|
|
5732
|
+
constructor(opts = {}) {
|
|
5733
|
+
this.paths = new ClaudeCpPaths(opts.home ?? homedir10());
|
|
5734
|
+
this.accountOptions = {
|
|
5735
|
+
...opts.accountUuid !== void 0 ? { accountUuid: opts.accountUuid } : {},
|
|
5736
|
+
...opts.orgUuid !== void 0 ? { orgUuid: opts.orgUuid } : {}
|
|
5737
|
+
};
|
|
5738
|
+
this.rushBinaryResolver = opts.rushBinaryResolver ?? defaultRushBinaryResolver;
|
|
5739
|
+
this.now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
5740
|
+
if (opts.marketplaceSource !== void 0) {
|
|
5741
|
+
this.marketplaceSource = opts.marketplaceSource;
|
|
5742
|
+
}
|
|
5743
|
+
}
|
|
5744
|
+
async detect() {
|
|
5745
|
+
return await this.resolveAccount() !== null;
|
|
5746
|
+
}
|
|
5747
|
+
async isInstalled(ref) {
|
|
5748
|
+
const account = await this.resolveAccount();
|
|
5749
|
+
if (!account) return false;
|
|
5750
|
+
const data = await this.readInstalledPlugins(account);
|
|
5751
|
+
const entries = data.plugins[pluginKey(ref)];
|
|
5752
|
+
return Array.isArray(entries) && entries.some((e) => e.scope === "user");
|
|
5753
|
+
}
|
|
5754
|
+
async list() {
|
|
5755
|
+
const account = await this.resolveAccount();
|
|
5756
|
+
if (!account) return [];
|
|
5757
|
+
const data = await this.readInstalledPlugins(account);
|
|
5758
|
+
const out = [];
|
|
5759
|
+
for (const [key, entries] of Object.entries(data.plugins)) {
|
|
5760
|
+
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
5761
|
+
const ref = parsePluginKey2(key);
|
|
5762
|
+
if (!ref) continue;
|
|
5763
|
+
const pick = entries.find((e) => e.scope === "user") ?? entries[0];
|
|
5764
|
+
if (!pick) continue;
|
|
5765
|
+
out.push({
|
|
5766
|
+
ref,
|
|
5767
|
+
version: pick.version,
|
|
5768
|
+
installedAt: pick.installedAt,
|
|
5769
|
+
targets: [this.target]
|
|
5770
|
+
});
|
|
5771
|
+
}
|
|
5772
|
+
out.sort((a, b) => {
|
|
5773
|
+
const ak = pluginKey(a.ref);
|
|
5774
|
+
const bk = pluginKey(b.ref);
|
|
5775
|
+
return ak < bk ? -1 : ak > bk ? 1 : 0;
|
|
5776
|
+
});
|
|
5777
|
+
return out;
|
|
5778
|
+
}
|
|
5779
|
+
async install(plugin, opts = {}) {
|
|
5780
|
+
const account = await this.resolveAccount();
|
|
5781
|
+
if (!account) {
|
|
5782
|
+
return {
|
|
5783
|
+
target: this.target,
|
|
5784
|
+
status: "skipped",
|
|
5785
|
+
included: [],
|
|
5786
|
+
skipped: [],
|
|
5787
|
+
artifacts: { files: [], mcpKeys: [] }
|
|
5788
|
+
};
|
|
5789
|
+
}
|
|
5790
|
+
const guardError = assertSafePathComponents2(plugin.ref, plugin.version);
|
|
5791
|
+
if (guardError) return this.buildFailedResult(guardError);
|
|
5792
|
+
if (!opts.force && !opts.dryRun) {
|
|
5793
|
+
try {
|
|
5794
|
+
if (await this.isAlreadyInstalledAtVersion(account, plugin)) {
|
|
5795
|
+
const files = [];
|
|
5796
|
+
if (await this.upsertKnownMarketplace(account, plugin.ref)) {
|
|
5797
|
+
pushUnique(
|
|
5798
|
+
files,
|
|
5799
|
+
this.paths.knownMarketplacesJson(
|
|
5800
|
+
account.accountUuid,
|
|
5801
|
+
account.orgUuid
|
|
5802
|
+
)
|
|
5803
|
+
);
|
|
5804
|
+
}
|
|
5805
|
+
if (await this.enablePlugin(account, plugin.ref)) {
|
|
5806
|
+
pushUnique(
|
|
5807
|
+
files,
|
|
5808
|
+
this.paths.coworkSettingsJson(
|
|
5809
|
+
account.accountUuid,
|
|
5810
|
+
account.orgUuid
|
|
5811
|
+
)
|
|
5812
|
+
);
|
|
5813
|
+
}
|
|
5814
|
+
return {
|
|
5815
|
+
target: this.target,
|
|
5816
|
+
status: "ok",
|
|
5817
|
+
included: this.computeIncluded(plugin),
|
|
5818
|
+
skipped: this.computeSkipped(plugin),
|
|
5819
|
+
artifacts: { files, mcpKeys: [] },
|
|
5820
|
+
...account.warnings.length > 0 ? { notes: account.warnings } : {}
|
|
5821
|
+
};
|
|
5822
|
+
}
|
|
5823
|
+
} catch (err) {
|
|
5824
|
+
return this.buildFailedResult(err);
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
if (opts.dryRun) {
|
|
5828
|
+
try {
|
|
5829
|
+
const result = await this.computeDryRun(account, plugin);
|
|
5830
|
+
return appendNotes(result, account.warnings);
|
|
5831
|
+
} catch (err) {
|
|
5832
|
+
return this.buildFailedResult(err);
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
5835
|
+
let rollback;
|
|
5836
|
+
try {
|
|
5837
|
+
rollback = await this.captureRollback(account, plugin.ref);
|
|
5838
|
+
} catch (err) {
|
|
5839
|
+
return this.buildFailedResult(err);
|
|
5840
|
+
}
|
|
5841
|
+
try {
|
|
5842
|
+
const writtenFiles = [];
|
|
5843
|
+
const pluginDir = this.paths.pluginDir(
|
|
5844
|
+
account.accountUuid,
|
|
5845
|
+
account.orgUuid,
|
|
5846
|
+
plugin.ref
|
|
5847
|
+
);
|
|
5848
|
+
await mkdir5(pluginDir, { recursive: true });
|
|
5849
|
+
pushUnique(writtenFiles, pluginDir);
|
|
5850
|
+
const copied = await copyPluginSourceTracked(plugin.sourceDir, pluginDir);
|
|
5851
|
+
for (const file2 of copied) pushUnique(writtenFiles, file2);
|
|
5852
|
+
const mcpServers = await this.resolveMcpServers(plugin);
|
|
5853
|
+
const mcpKeys = mcpServers ? Object.keys(mcpServers) : [];
|
|
5854
|
+
if (mcpServers && mcpKeys.length > 0) {
|
|
5855
|
+
const mcpPath = this.paths.pluginMcpJsonPath(
|
|
5856
|
+
account.accountUuid,
|
|
5857
|
+
account.orgUuid,
|
|
5858
|
+
plugin.ref
|
|
5859
|
+
);
|
|
5860
|
+
await writeJsonFile(mcpPath, { mcpServers });
|
|
5861
|
+
pushUnique(writtenFiles, mcpPath);
|
|
5862
|
+
}
|
|
5863
|
+
const manifestPath = this.paths.pluginManifestPath(
|
|
5864
|
+
account.accountUuid,
|
|
5865
|
+
account.orgUuid,
|
|
5866
|
+
plugin.ref
|
|
5867
|
+
);
|
|
5868
|
+
await writeJsonFile(manifestPath, buildPluginJson(plugin, mcpKeys));
|
|
5869
|
+
pushUnique(writtenFiles, manifestPath);
|
|
5870
|
+
const marketplaceManifestPath = this.paths.marketplaceManifestPath(
|
|
5871
|
+
account.accountUuid,
|
|
5872
|
+
account.orgUuid,
|
|
5873
|
+
plugin.ref.marketplace
|
|
5874
|
+
);
|
|
5875
|
+
await writeJsonFile(
|
|
5876
|
+
marketplaceManifestPath,
|
|
5877
|
+
await this.buildMarketplaceManifest(plugin.ref)
|
|
5878
|
+
);
|
|
5879
|
+
pushUnique(writtenFiles, marketplaceManifestPath);
|
|
5880
|
+
if (await this.upsertKnownMarketplace(account, plugin.ref)) {
|
|
5881
|
+
pushUnique(
|
|
5882
|
+
writtenFiles,
|
|
5883
|
+
this.paths.knownMarketplacesJson(account.accountUuid, account.orgUuid)
|
|
5884
|
+
);
|
|
5885
|
+
}
|
|
5886
|
+
await this.upsertInstalledPlugin(account, plugin);
|
|
5887
|
+
pushUnique(
|
|
5888
|
+
writtenFiles,
|
|
5889
|
+
this.paths.installedPluginsJson(account.accountUuid, account.orgUuid)
|
|
5890
|
+
);
|
|
5891
|
+
if (await this.enablePlugin(account, plugin.ref)) {
|
|
5892
|
+
pushUnique(
|
|
5893
|
+
writtenFiles,
|
|
5894
|
+
this.paths.coworkSettingsJson(account.accountUuid, account.orgUuid)
|
|
5895
|
+
);
|
|
5896
|
+
}
|
|
5897
|
+
await this.finalizeRollback(rollback);
|
|
5898
|
+
return {
|
|
5899
|
+
target: this.target,
|
|
5900
|
+
status: "ok",
|
|
5901
|
+
included: this.computeIncluded(plugin),
|
|
5902
|
+
skipped: this.computeSkipped(plugin),
|
|
5903
|
+
artifacts: { files: writtenFiles, mcpKeys },
|
|
5904
|
+
...account.warnings.length > 0 ? { notes: account.warnings } : {}
|
|
5905
|
+
};
|
|
5906
|
+
} catch (err) {
|
|
5907
|
+
await this.applyRollback(rollback).catch(() => {
|
|
5908
|
+
});
|
|
5909
|
+
return this.buildFailedResult(err);
|
|
5910
|
+
}
|
|
5911
|
+
}
|
|
5912
|
+
async uninstall(ref, opts = {}) {
|
|
5913
|
+
const account = await this.resolveAccount();
|
|
5914
|
+
if (!account) {
|
|
5915
|
+
return {
|
|
5916
|
+
target: this.target,
|
|
5917
|
+
status: "ok",
|
|
5918
|
+
artifacts: { files: [], mcpKeys: [] }
|
|
5919
|
+
};
|
|
5920
|
+
}
|
|
5921
|
+
const guardError = assertSafePathComponents2(ref, "0.0.0");
|
|
5922
|
+
if (guardError) return this.buildFailedUninstallResult(guardError);
|
|
5923
|
+
try {
|
|
5924
|
+
const mcpKeys = await this.collectMcpKeys(account, ref);
|
|
5925
|
+
const files = await this.computeUninstallFiles(account, ref);
|
|
5926
|
+
if (opts.dryRun) {
|
|
5927
|
+
return {
|
|
5928
|
+
target: this.target,
|
|
5929
|
+
status: "ok",
|
|
5930
|
+
artifacts: { files, mcpKeys }
|
|
5931
|
+
};
|
|
5932
|
+
}
|
|
5933
|
+
const touchedFiles = [];
|
|
5934
|
+
const removedFromRegistry = await this.removeInstalledPlugin(
|
|
5935
|
+
account,
|
|
5936
|
+
ref
|
|
5937
|
+
);
|
|
5938
|
+
if (removedFromRegistry) {
|
|
5939
|
+
pushUnique(
|
|
5940
|
+
touchedFiles,
|
|
5941
|
+
this.paths.installedPluginsJson(account.accountUuid, account.orgUuid)
|
|
5942
|
+
);
|
|
5943
|
+
}
|
|
5944
|
+
if (await this.disablePlugin(account, ref)) {
|
|
5945
|
+
pushUnique(
|
|
5946
|
+
touchedFiles,
|
|
5947
|
+
this.paths.coworkSettingsJson(account.accountUuid, account.orgUuid)
|
|
5948
|
+
);
|
|
5949
|
+
}
|
|
5950
|
+
const pluginDir = this.paths.pluginDir(
|
|
5951
|
+
account.accountUuid,
|
|
5952
|
+
account.orgUuid,
|
|
5953
|
+
ref
|
|
5954
|
+
);
|
|
5955
|
+
if (await pathExists(pluginDir)) {
|
|
5956
|
+
await rm6(pluginDir, { recursive: true, force: true });
|
|
5957
|
+
pushUnique(touchedFiles, pluginDir);
|
|
5958
|
+
}
|
|
5959
|
+
if (removedFromRegistry && !await this.marketplaceStillReferenced(account, ref.marketplace)) {
|
|
5960
|
+
const marketplaceDir = this.paths.marketplaceDir(
|
|
5961
|
+
account.accountUuid,
|
|
5962
|
+
account.orgUuid,
|
|
5963
|
+
ref.marketplace
|
|
5964
|
+
);
|
|
5965
|
+
await rm6(marketplaceDir, { recursive: true, force: true });
|
|
5966
|
+
pushUnique(touchedFiles, marketplaceDir);
|
|
5967
|
+
if (await this.removeKnownMarketplace(account, ref.marketplace)) {
|
|
5968
|
+
pushUnique(
|
|
5969
|
+
touchedFiles,
|
|
5970
|
+
this.paths.knownMarketplacesJson(
|
|
5971
|
+
account.accountUuid,
|
|
5972
|
+
account.orgUuid
|
|
5973
|
+
)
|
|
5974
|
+
);
|
|
5975
|
+
}
|
|
5976
|
+
}
|
|
5977
|
+
return {
|
|
5978
|
+
target: this.target,
|
|
5979
|
+
status: "ok",
|
|
5980
|
+
artifacts: { files: touchedFiles, mcpKeys }
|
|
5981
|
+
};
|
|
5982
|
+
} catch (err) {
|
|
5983
|
+
return this.buildFailedUninstallResult(err);
|
|
5984
|
+
}
|
|
5985
|
+
}
|
|
5986
|
+
async resolveAccount() {
|
|
5987
|
+
return resolveClaudeCpAccount(this.paths, this.accountOptions);
|
|
5988
|
+
}
|
|
5989
|
+
async readInstalledPlugins(account) {
|
|
5990
|
+
const filePath = this.paths.installedPluginsJson(
|
|
5991
|
+
account.accountUuid,
|
|
5992
|
+
account.orgUuid
|
|
5993
|
+
);
|
|
5994
|
+
if (!await pathExists(filePath)) return { version: 2, plugins: {} };
|
|
5995
|
+
const { data } = await readJsonFile(filePath, () => ({
|
|
5996
|
+
version: 2,
|
|
5997
|
+
plugins: {}
|
|
5998
|
+
}));
|
|
5999
|
+
if (data.version !== 2) {
|
|
6000
|
+
throw new Error(
|
|
6001
|
+
`${filePath} has unsupported version ${String(data.version)}; expected 2`
|
|
6002
|
+
);
|
|
6003
|
+
}
|
|
6004
|
+
if (!data.plugins || typeof data.plugins !== "object" || Array.isArray(data.plugins)) {
|
|
6005
|
+
throw new Error(`${filePath} has invalid plugins shape`);
|
|
6006
|
+
}
|
|
6007
|
+
return data;
|
|
6008
|
+
}
|
|
6009
|
+
async readCoworkSettings(account) {
|
|
6010
|
+
const filePath = this.paths.coworkSettingsJson(
|
|
6011
|
+
account.accountUuid,
|
|
6012
|
+
account.orgUuid
|
|
6013
|
+
);
|
|
6014
|
+
if (!await pathExists(filePath)) return {};
|
|
6015
|
+
const { data } = await readJsonFile(
|
|
6016
|
+
filePath,
|
|
6017
|
+
() => ({})
|
|
6018
|
+
);
|
|
6019
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
6020
|
+
throw new Error(`${filePath} has invalid cowork settings shape`);
|
|
6021
|
+
}
|
|
6022
|
+
if (data.enabledPlugins !== void 0 && (!data.enabledPlugins || typeof data.enabledPlugins !== "object" || Array.isArray(data.enabledPlugins))) {
|
|
6023
|
+
throw new Error(`${filePath} has invalid enabledPlugins shape`);
|
|
6024
|
+
}
|
|
6025
|
+
return data;
|
|
6026
|
+
}
|
|
6027
|
+
async enablePlugin(account, ref) {
|
|
6028
|
+
const filePath = this.paths.coworkSettingsJson(
|
|
6029
|
+
account.accountUuid,
|
|
6030
|
+
account.orgUuid
|
|
6031
|
+
);
|
|
6032
|
+
const data = await this.readCoworkSettings(account);
|
|
6033
|
+
const key = pluginKey(ref);
|
|
6034
|
+
if (data.enabledPlugins?.[key] === true) return false;
|
|
6035
|
+
await writeJsonFile(filePath, {
|
|
6036
|
+
...data,
|
|
6037
|
+
enabledPlugins: {
|
|
6038
|
+
...data.enabledPlugins ?? {},
|
|
6039
|
+
[key]: true
|
|
6040
|
+
}
|
|
6041
|
+
});
|
|
6042
|
+
return true;
|
|
6043
|
+
}
|
|
6044
|
+
async disablePlugin(account, ref) {
|
|
6045
|
+
const filePath = this.paths.coworkSettingsJson(
|
|
6046
|
+
account.accountUuid,
|
|
6047
|
+
account.orgUuid
|
|
6048
|
+
);
|
|
6049
|
+
if (!await pathExists(filePath)) return false;
|
|
6050
|
+
const data = await this.readCoworkSettings(account);
|
|
6051
|
+
const key = pluginKey(ref);
|
|
6052
|
+
if (data.enabledPlugins?.[key] !== true) return false;
|
|
6053
|
+
const nextEnabledPlugins = { ...data.enabledPlugins };
|
|
6054
|
+
delete nextEnabledPlugins[key];
|
|
6055
|
+
const next = { ...data };
|
|
6056
|
+
if (Object.keys(nextEnabledPlugins).length > 0) {
|
|
6057
|
+
next.enabledPlugins = nextEnabledPlugins;
|
|
6058
|
+
} else {
|
|
6059
|
+
delete next.enabledPlugins;
|
|
6060
|
+
}
|
|
6061
|
+
await writeJsonFile(filePath, next);
|
|
6062
|
+
return true;
|
|
6063
|
+
}
|
|
6064
|
+
async isAlreadyInstalledAtVersion(account, plugin) {
|
|
6065
|
+
const data = await this.readInstalledPlugins(account);
|
|
6066
|
+
const entries = data.plugins[pluginKey(plugin.ref)];
|
|
6067
|
+
if (!Array.isArray(entries)) return false;
|
|
6068
|
+
return entries.some(
|
|
6069
|
+
(entry) => entry.scope === "user" && entry.version === plugin.version
|
|
6070
|
+
);
|
|
6071
|
+
}
|
|
6072
|
+
async upsertInstalledPlugin(account, plugin) {
|
|
6073
|
+
const filePath = this.paths.installedPluginsJson(
|
|
6074
|
+
account.accountUuid,
|
|
6075
|
+
account.orgUuid
|
|
6076
|
+
);
|
|
6077
|
+
const data = await this.readInstalledPlugins(account);
|
|
6078
|
+
const key = pluginKey(plugin.ref);
|
|
6079
|
+
const nowIso = this.now();
|
|
6080
|
+
const installPath = this.paths.pluginDir(
|
|
6081
|
+
account.accountUuid,
|
|
6082
|
+
account.orgUuid,
|
|
6083
|
+
plugin.ref
|
|
6084
|
+
);
|
|
6085
|
+
const entries = data.plugins[key] ?? [];
|
|
6086
|
+
const existingUser = entries.find((entry) => entry.scope === "user");
|
|
6087
|
+
const nextUser = {
|
|
6088
|
+
scope: "user",
|
|
6089
|
+
installPath,
|
|
6090
|
+
version: plugin.version,
|
|
6091
|
+
installedAt: existingUser?.installedAt ?? nowIso,
|
|
6092
|
+
lastUpdated: nowIso
|
|
6093
|
+
};
|
|
6094
|
+
const nextEntries = [
|
|
6095
|
+
nextUser,
|
|
6096
|
+
...entries.filter((entry) => entry.scope !== "user")
|
|
6097
|
+
];
|
|
6098
|
+
data.plugins = { ...data.plugins, [key]: nextEntries };
|
|
6099
|
+
await writeJsonFile(filePath, data);
|
|
6100
|
+
}
|
|
6101
|
+
async removeInstalledPlugin(account, ref) {
|
|
6102
|
+
const filePath = this.paths.installedPluginsJson(
|
|
6103
|
+
account.accountUuid,
|
|
6104
|
+
account.orgUuid
|
|
6105
|
+
);
|
|
6106
|
+
if (!await pathExists(filePath)) return false;
|
|
6107
|
+
const data = await this.readInstalledPlugins(account);
|
|
6108
|
+
const key = pluginKey(ref);
|
|
6109
|
+
const entries = data.plugins[key];
|
|
6110
|
+
if (!Array.isArray(entries)) return false;
|
|
6111
|
+
const nextEntries = entries.filter((entry) => entry.scope !== "user");
|
|
6112
|
+
if (nextEntries.length === entries.length) return false;
|
|
6113
|
+
if (nextEntries.length > 0) {
|
|
6114
|
+
data.plugins = { ...data.plugins, [key]: nextEntries };
|
|
6115
|
+
} else {
|
|
6116
|
+
const nextPlugins = { ...data.plugins };
|
|
6117
|
+
delete nextPlugins[key];
|
|
6118
|
+
data.plugins = nextPlugins;
|
|
6119
|
+
}
|
|
6120
|
+
await writeJsonFile(filePath, data);
|
|
6121
|
+
return true;
|
|
6122
|
+
}
|
|
6123
|
+
async readKnownMarketplaces(account) {
|
|
6124
|
+
const filePath = this.paths.knownMarketplacesJson(
|
|
6125
|
+
account.accountUuid,
|
|
6126
|
+
account.orgUuid
|
|
6127
|
+
);
|
|
6128
|
+
if (!await pathExists(filePath)) return {};
|
|
6129
|
+
const { data } = await readJsonFile(
|
|
6130
|
+
filePath,
|
|
6131
|
+
() => ({})
|
|
6132
|
+
);
|
|
6133
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
6134
|
+
throw new Error(`${filePath} has invalid known marketplaces shape`);
|
|
6135
|
+
}
|
|
6136
|
+
return data;
|
|
6137
|
+
}
|
|
6138
|
+
async upsertKnownMarketplace(account, ref) {
|
|
6139
|
+
const filePath = this.paths.knownMarketplacesJson(
|
|
6140
|
+
account.accountUuid,
|
|
6141
|
+
account.orgUuid
|
|
6142
|
+
);
|
|
6143
|
+
const data = await this.readKnownMarketplaces(account);
|
|
6144
|
+
if (Object.hasOwn(data, ref.marketplace)) return false;
|
|
6145
|
+
await writeJsonFile(filePath, {
|
|
6146
|
+
...data,
|
|
6147
|
+
[ref.marketplace]: this.buildKnownMarketplaceEntry(account, ref)
|
|
6148
|
+
});
|
|
6149
|
+
return true;
|
|
6150
|
+
}
|
|
6151
|
+
async removeKnownMarketplace(account, marketplace) {
|
|
6152
|
+
const filePath = this.paths.knownMarketplacesJson(
|
|
6153
|
+
account.accountUuid,
|
|
6154
|
+
account.orgUuid
|
|
6155
|
+
);
|
|
6156
|
+
if (!await pathExists(filePath)) return false;
|
|
6157
|
+
const data = await this.readKnownMarketplaces(account);
|
|
6158
|
+
if (!Object.hasOwn(data, marketplace)) return false;
|
|
6159
|
+
const next = { ...data };
|
|
6160
|
+
delete next[marketplace];
|
|
6161
|
+
await writeJsonFile(filePath, next);
|
|
6162
|
+
return true;
|
|
6163
|
+
}
|
|
6164
|
+
buildKnownMarketplaceEntry(account, ref) {
|
|
6165
|
+
const installLocation = this.paths.marketplaceDir(
|
|
6166
|
+
account.accountUuid,
|
|
6167
|
+
account.orgUuid,
|
|
6168
|
+
ref.marketplace
|
|
6169
|
+
);
|
|
6170
|
+
return {
|
|
6171
|
+
source: {
|
|
6172
|
+
source: "directory",
|
|
6173
|
+
path: this.marketplaceSource?.rootDir ?? installLocation
|
|
6174
|
+
},
|
|
6175
|
+
installLocation,
|
|
6176
|
+
lastUpdated: this.now(),
|
|
6177
|
+
autoUpdate: false
|
|
6178
|
+
};
|
|
6179
|
+
}
|
|
6180
|
+
async marketplaceStillReferenced(account, marketplace) {
|
|
6181
|
+
const data = await this.readInstalledPlugins(account);
|
|
6182
|
+
const suffix = `@${marketplace}`;
|
|
6183
|
+
return Object.entries(data.plugins).some(
|
|
6184
|
+
([key, entries]) => key.endsWith(suffix) && Array.isArray(entries) && entries.length > 0
|
|
6185
|
+
);
|
|
6186
|
+
}
|
|
6187
|
+
async computeDryRun(account, plugin) {
|
|
6188
|
+
const files = [];
|
|
6189
|
+
const pluginDir = this.paths.pluginDir(
|
|
6190
|
+
account.accountUuid,
|
|
6191
|
+
account.orgUuid,
|
|
6192
|
+
plugin.ref
|
|
6193
|
+
);
|
|
6194
|
+
pushUnique(files, pluginDir);
|
|
6195
|
+
const planned = await listDirFilesRecursive(plugin.sourceDir, pluginDir, {
|
|
6196
|
+
skipTopLevel: /* @__PURE__ */ new Set(["rules"])
|
|
6197
|
+
});
|
|
6198
|
+
for (const file2 of planned) pushUnique(files, file2);
|
|
6199
|
+
const mcpServers = await this.resolveMcpServers(plugin);
|
|
6200
|
+
const mcpKeys = mcpServers ? Object.keys(mcpServers) : [];
|
|
6201
|
+
if (mcpServers && mcpKeys.length > 0) {
|
|
6202
|
+
pushUnique(
|
|
6203
|
+
files,
|
|
6204
|
+
this.paths.pluginMcpJsonPath(
|
|
6205
|
+
account.accountUuid,
|
|
6206
|
+
account.orgUuid,
|
|
6207
|
+
plugin.ref
|
|
6208
|
+
)
|
|
6209
|
+
);
|
|
6210
|
+
}
|
|
6211
|
+
pushUnique(
|
|
6212
|
+
files,
|
|
6213
|
+
this.paths.pluginManifestPath(
|
|
6214
|
+
account.accountUuid,
|
|
6215
|
+
account.orgUuid,
|
|
6216
|
+
plugin.ref
|
|
6217
|
+
)
|
|
6218
|
+
);
|
|
6219
|
+
pushUnique(
|
|
6220
|
+
files,
|
|
6221
|
+
this.paths.marketplaceManifestPath(
|
|
6222
|
+
account.accountUuid,
|
|
6223
|
+
account.orgUuid,
|
|
6224
|
+
plugin.ref.marketplace
|
|
6225
|
+
)
|
|
6226
|
+
);
|
|
6227
|
+
pushUnique(
|
|
6228
|
+
files,
|
|
6229
|
+
this.paths.knownMarketplacesJson(account.accountUuid, account.orgUuid)
|
|
6230
|
+
);
|
|
6231
|
+
pushUnique(
|
|
6232
|
+
files,
|
|
6233
|
+
this.paths.installedPluginsJson(account.accountUuid, account.orgUuid)
|
|
6234
|
+
);
|
|
6235
|
+
pushUnique(
|
|
6236
|
+
files,
|
|
6237
|
+
this.paths.coworkSettingsJson(account.accountUuid, account.orgUuid)
|
|
6238
|
+
);
|
|
6239
|
+
return {
|
|
6240
|
+
target: this.target,
|
|
6241
|
+
status: "ok",
|
|
6242
|
+
included: this.computeIncluded(plugin),
|
|
6243
|
+
skipped: this.computeSkipped(plugin),
|
|
6244
|
+
artifacts: { files, mcpKeys }
|
|
6245
|
+
};
|
|
6246
|
+
}
|
|
6247
|
+
async computeUninstallFiles(account, ref) {
|
|
6248
|
+
const files = [];
|
|
6249
|
+
const hasInstalledEntry = await this.hasInstalledPluginEntry(account, ref);
|
|
6250
|
+
if (hasInstalledEntry) {
|
|
6251
|
+
pushUnique(
|
|
6252
|
+
files,
|
|
6253
|
+
this.paths.installedPluginsJson(account.accountUuid, account.orgUuid)
|
|
6254
|
+
);
|
|
6255
|
+
}
|
|
6256
|
+
if (await this.hasEnabledPluginEntry(account, ref)) {
|
|
6257
|
+
pushUnique(
|
|
6258
|
+
files,
|
|
6259
|
+
this.paths.coworkSettingsJson(account.accountUuid, account.orgUuid)
|
|
6260
|
+
);
|
|
6261
|
+
}
|
|
6262
|
+
const pluginDir = this.paths.pluginDir(
|
|
6263
|
+
account.accountUuid,
|
|
6264
|
+
account.orgUuid,
|
|
6265
|
+
ref
|
|
6266
|
+
);
|
|
6267
|
+
if (await pathExists(pluginDir)) pushUnique(files, pluginDir);
|
|
6268
|
+
if (hasInstalledEntry && !await this.marketplaceWouldStillBeReferencedAfterRemoval(
|
|
6269
|
+
account,
|
|
6270
|
+
ref.marketplace,
|
|
6271
|
+
pluginKey(ref)
|
|
6272
|
+
)) {
|
|
6273
|
+
pushUnique(
|
|
6274
|
+
files,
|
|
6275
|
+
this.paths.marketplaceDir(
|
|
6276
|
+
account.accountUuid,
|
|
6277
|
+
account.orgUuid,
|
|
6278
|
+
ref.marketplace
|
|
6279
|
+
)
|
|
6280
|
+
);
|
|
6281
|
+
if (await this.hasKnownMarketplaceEntry(account, ref.marketplace)) {
|
|
6282
|
+
pushUnique(
|
|
6283
|
+
files,
|
|
6284
|
+
this.paths.knownMarketplacesJson(account.accountUuid, account.orgUuid)
|
|
6285
|
+
);
|
|
6286
|
+
}
|
|
6287
|
+
}
|
|
6288
|
+
return files;
|
|
6289
|
+
}
|
|
6290
|
+
async hasEnabledPluginEntry(account, ref) {
|
|
6291
|
+
const data = await this.readCoworkSettings(account);
|
|
6292
|
+
return data.enabledPlugins?.[pluginKey(ref)] === true;
|
|
6293
|
+
}
|
|
6294
|
+
async hasInstalledPluginEntry(account, ref) {
|
|
6295
|
+
const data = await this.readInstalledPlugins(account);
|
|
6296
|
+
const entries = data.plugins[pluginKey(ref)];
|
|
6297
|
+
return Array.isArray(entries) && entries.some((entry) => entry.scope === "user");
|
|
6298
|
+
}
|
|
6299
|
+
async hasKnownMarketplaceEntry(account, marketplace) {
|
|
6300
|
+
const data = await this.readKnownMarketplaces(account);
|
|
6301
|
+
return Object.hasOwn(data, marketplace);
|
|
6302
|
+
}
|
|
6303
|
+
async marketplaceWouldStillBeReferencedAfterRemoval(account, marketplace, removedKey) {
|
|
6304
|
+
const data = await this.readInstalledPlugins(account);
|
|
6305
|
+
const suffix = `@${marketplace}`;
|
|
6306
|
+
for (const [key, entries] of Object.entries(data.plugins)) {
|
|
6307
|
+
if (key === removedKey) continue;
|
|
6308
|
+
if (key.endsWith(suffix) && Array.isArray(entries) && entries.length > 0) {
|
|
6309
|
+
return true;
|
|
6310
|
+
}
|
|
6311
|
+
}
|
|
6312
|
+
return false;
|
|
6313
|
+
}
|
|
6314
|
+
async resolveMcpServers(plugin) {
|
|
6315
|
+
if (typeof plugin.manifest.mcpServers === "string") {
|
|
6316
|
+
const external = await readExternalMcpServers(
|
|
6317
|
+
plugin.sourceDir,
|
|
6318
|
+
plugin.manifest.mcpServers
|
|
6319
|
+
);
|
|
6320
|
+
if (!external) return void 0;
|
|
6321
|
+
return normalizeClaudeMcpServers(
|
|
6322
|
+
plugin.ref,
|
|
6323
|
+
{ ...plugin.manifest, mcpServers: external },
|
|
6324
|
+
this.rushBinaryResolver
|
|
6325
|
+
);
|
|
6326
|
+
}
|
|
6327
|
+
return normalizeClaudeMcpServers(
|
|
6328
|
+
plugin.ref,
|
|
6329
|
+
plugin.manifest,
|
|
6330
|
+
this.rushBinaryResolver
|
|
6331
|
+
);
|
|
6332
|
+
}
|
|
6333
|
+
computeIncluded(plugin) {
|
|
6334
|
+
return plugin.capabilities.filter((cap) => cap !== "rules");
|
|
6335
|
+
}
|
|
6336
|
+
computeSkipped(plugin) {
|
|
6337
|
+
return plugin.capabilities.includes("rules") ? ["rules"] : [];
|
|
6338
|
+
}
|
|
6339
|
+
async buildMarketplaceManifest(ref) {
|
|
6340
|
+
if (this.marketplaceSource) {
|
|
6341
|
+
const sourceManifest = resolve11(
|
|
6342
|
+
this.marketplaceSource.rootDir,
|
|
6343
|
+
".claude-plugin",
|
|
6344
|
+
"marketplace.json"
|
|
6345
|
+
);
|
|
6346
|
+
const raw = await readFile6(sourceManifest, "utf8");
|
|
6347
|
+
return JSON.parse(raw);
|
|
6348
|
+
}
|
|
6349
|
+
return {
|
|
6350
|
+
name: ref.marketplace,
|
|
6351
|
+
plugins: [{ name: ref.name, source: `./${ref.name}` }]
|
|
6352
|
+
};
|
|
6353
|
+
}
|
|
6354
|
+
async collectMcpKeys(account, ref) {
|
|
6355
|
+
const mcpPath = this.paths.pluginMcpJsonPath(
|
|
6356
|
+
account.accountUuid,
|
|
6357
|
+
account.orgUuid,
|
|
6358
|
+
ref
|
|
6359
|
+
);
|
|
6360
|
+
if (!await pathExists(mcpPath)) return [];
|
|
6361
|
+
try {
|
|
6362
|
+
const raw = await readFile6(mcpPath, "utf8");
|
|
6363
|
+
const parsed = JSON.parse(raw);
|
|
6364
|
+
if (parsed.mcpServers && typeof parsed.mcpServers === "object") {
|
|
6365
|
+
return Object.keys(parsed.mcpServers);
|
|
6366
|
+
}
|
|
6367
|
+
} catch {
|
|
6368
|
+
return [];
|
|
6369
|
+
}
|
|
6370
|
+
return [];
|
|
6371
|
+
}
|
|
6372
|
+
async captureRollback(account, ref) {
|
|
6373
|
+
const marketplaceDir = this.paths.marketplaceDir(
|
|
6374
|
+
account.accountUuid,
|
|
6375
|
+
account.orgUuid,
|
|
6376
|
+
ref.marketplace
|
|
6377
|
+
);
|
|
6378
|
+
const marketplaceDirExisted = await pathExists(marketplaceDir);
|
|
6379
|
+
const pluginDir = this.paths.pluginDir(
|
|
6380
|
+
account.accountUuid,
|
|
6381
|
+
account.orgUuid,
|
|
6382
|
+
ref
|
|
6383
|
+
);
|
|
6384
|
+
const files = [
|
|
6385
|
+
await snapshotFile(
|
|
6386
|
+
this.paths.marketplaceManifestPath(
|
|
6387
|
+
account.accountUuid,
|
|
6388
|
+
account.orgUuid,
|
|
6389
|
+
ref.marketplace
|
|
6390
|
+
)
|
|
6391
|
+
),
|
|
6392
|
+
await snapshotFile(
|
|
6393
|
+
this.paths.knownMarketplacesJson(account.accountUuid, account.orgUuid)
|
|
6394
|
+
),
|
|
6395
|
+
await snapshotFile(
|
|
6396
|
+
this.paths.installedPluginsJson(account.accountUuid, account.orgUuid)
|
|
6397
|
+
),
|
|
6398
|
+
await snapshotFile(
|
|
6399
|
+
this.paths.coworkSettingsJson(account.accountUuid, account.orgUuid)
|
|
6400
|
+
)
|
|
6401
|
+
];
|
|
6402
|
+
return {
|
|
6403
|
+
pluginDir: await moveDirAside(pluginDir),
|
|
6404
|
+
marketplaceDir,
|
|
6405
|
+
marketplaceDirExisted,
|
|
6406
|
+
files
|
|
6407
|
+
};
|
|
6408
|
+
}
|
|
6409
|
+
async finalizeRollback(snapshot) {
|
|
6410
|
+
if (snapshot.pluginDir.backupPath) {
|
|
6411
|
+
await rm6(snapshot.pluginDir.backupPath, { recursive: true, force: true });
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6414
|
+
async applyRollback(snapshot) {
|
|
6415
|
+
await restoreMovedDir(snapshot.pluginDir);
|
|
6416
|
+
for (const file2 of snapshot.files) {
|
|
6417
|
+
await restoreFile(file2);
|
|
6418
|
+
}
|
|
6419
|
+
if (!snapshot.marketplaceDirExisted) {
|
|
6420
|
+
await rm6(snapshot.marketplaceDir, { recursive: true, force: true });
|
|
6421
|
+
}
|
|
6422
|
+
}
|
|
6423
|
+
buildFailedResult(err) {
|
|
6424
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6425
|
+
return {
|
|
6426
|
+
target: this.target,
|
|
6427
|
+
status: "failed",
|
|
6428
|
+
included: [],
|
|
6429
|
+
skipped: [],
|
|
6430
|
+
artifacts: { files: [], mcpKeys: [] },
|
|
6431
|
+
errors: [message]
|
|
6432
|
+
};
|
|
6433
|
+
}
|
|
6434
|
+
buildFailedUninstallResult(err) {
|
|
6435
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6436
|
+
return {
|
|
6437
|
+
target: this.target,
|
|
6438
|
+
status: "failed",
|
|
6439
|
+
artifacts: { files: [], mcpKeys: [] },
|
|
6440
|
+
errors: [message]
|
|
6441
|
+
};
|
|
6442
|
+
}
|
|
6443
|
+
};
|
|
6444
|
+
function buildPluginJson(plugin, mcpKeys) {
|
|
6445
|
+
const { mcpServers: _mcpServers, ...rest } = plugin.manifest;
|
|
6446
|
+
return {
|
|
6447
|
+
...rest,
|
|
6448
|
+
name: plugin.ref.name,
|
|
6449
|
+
version: plugin.version,
|
|
6450
|
+
...mcpKeys.length > 0 ? { mcpServers: "./.mcp.json" } : {}
|
|
6451
|
+
};
|
|
6452
|
+
}
|
|
6453
|
+
function appendNotes(result, notes) {
|
|
6454
|
+
if (notes.length === 0) return result;
|
|
6455
|
+
return { ...result, notes: [...result.notes ?? [], ...notes] };
|
|
6456
|
+
}
|
|
6457
|
+
function parsePluginKey2(key) {
|
|
6458
|
+
const at = key.lastIndexOf("@");
|
|
6459
|
+
if (at <= 0 || at === key.length - 1) return null;
|
|
6460
|
+
return { name: key.slice(0, at), marketplace: key.slice(at + 1) };
|
|
6461
|
+
}
|
|
6462
|
+
function assertSafePathComponents2(ref, version) {
|
|
6463
|
+
for (const [label, value] of [
|
|
6464
|
+
["plugin name", ref.name],
|
|
6465
|
+
["marketplace", ref.marketplace],
|
|
6466
|
+
["version", version]
|
|
6467
|
+
]) {
|
|
6468
|
+
if (value.length === 0 || value === "." || value === ".." || value.includes("/") || value.includes("\\")) {
|
|
6469
|
+
return new Error(`Invalid ${label} '${value}' for Claude-3p path`);
|
|
6470
|
+
}
|
|
5479
6471
|
}
|
|
5480
6472
|
return null;
|
|
5481
6473
|
}
|
|
5482
|
-
function
|
|
5483
|
-
|
|
6474
|
+
async function snapshotFile(path4) {
|
|
6475
|
+
if (!await pathExists(path4)) return { path: path4, existed: false, content: null };
|
|
6476
|
+
return { path: path4, existed: true, content: await readFile6(path4, "utf8") };
|
|
5484
6477
|
}
|
|
5485
|
-
function
|
|
5486
|
-
|
|
5487
|
-
|
|
6478
|
+
async function restoreFile(snapshot) {
|
|
6479
|
+
if (snapshot.existed) {
|
|
6480
|
+
await writeTextFileAtomic(snapshot.path, snapshot.content ?? "");
|
|
6481
|
+
return;
|
|
6482
|
+
}
|
|
6483
|
+
await rm6(snapshot.path, { force: true });
|
|
6484
|
+
await pruneEmptyParents(dirname5(snapshot.path), 2);
|
|
5488
6485
|
}
|
|
5489
|
-
function
|
|
5490
|
-
if (!
|
|
5491
|
-
|
|
6486
|
+
async function moveDirAside(path4) {
|
|
6487
|
+
if (!await pathExists(path4)) {
|
|
6488
|
+
return { path: path4, existed: false, backupPath: null };
|
|
6489
|
+
}
|
|
6490
|
+
const backupPath = `${path4}.rush-ai-backup-${randomUUID3()}`;
|
|
6491
|
+
await rename3(path4, backupPath);
|
|
6492
|
+
return { path: path4, existed: true, backupPath };
|
|
5492
6493
|
}
|
|
5493
|
-
function
|
|
5494
|
-
|
|
5495
|
-
if (
|
|
5496
|
-
|
|
5497
|
-
"Could not locate the skills/ directory. This is a packaging bug \u2014 please file an issue."
|
|
5498
|
-
);
|
|
5499
|
-
process.exit(2);
|
|
5500
|
-
return;
|
|
6494
|
+
async function restoreMovedDir(snapshot) {
|
|
6495
|
+
await rm6(snapshot.path, { recursive: true, force: true });
|
|
6496
|
+
if (snapshot.backupPath) {
|
|
6497
|
+
await rename3(snapshot.backupPath, snapshot.path);
|
|
5501
6498
|
}
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
6499
|
+
}
|
|
6500
|
+
async function copyPluginSourceTracked(srcDir, dstDir) {
|
|
6501
|
+
await mkdir5(dstDir, { recursive: true });
|
|
6502
|
+
await cp2(srcDir, dstDir, { recursive: true, force: true });
|
|
6503
|
+
await rm6(join9(dstDir, "rules"), { recursive: true, force: true });
|
|
6504
|
+
return listExistingDirPaths(dstDir);
|
|
6505
|
+
}
|
|
6506
|
+
async function listDirFilesRecursive(srcDir, dstDir, opts = {}) {
|
|
6507
|
+
const out = [dstDir];
|
|
6508
|
+
async function walk2(src, dst, depth) {
|
|
6509
|
+
const entries = await readdir4(src, { withFileTypes: true });
|
|
6510
|
+
for (const entry of entries) {
|
|
6511
|
+
if (depth === 0 && opts.skipTopLevel?.has(entry.name)) continue;
|
|
6512
|
+
const srcPath = join9(src, entry.name);
|
|
6513
|
+
const dstPath = join9(dst, entry.name);
|
|
6514
|
+
out.push(dstPath);
|
|
6515
|
+
if (entry.isDirectory()) await walk2(srcPath, dstPath, depth + 1);
|
|
6516
|
+
}
|
|
5506
6517
|
}
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
6518
|
+
await walk2(srcDir, dstDir, 0);
|
|
6519
|
+
return out;
|
|
6520
|
+
}
|
|
6521
|
+
async function listExistingDirPaths(dir) {
|
|
6522
|
+
const out = [dir];
|
|
6523
|
+
async function walk2(current) {
|
|
6524
|
+
const entries = await readdir4(current, { withFileTypes: true });
|
|
6525
|
+
for (const entry of entries) {
|
|
6526
|
+
const p = join9(current, entry.name);
|
|
6527
|
+
out.push(p);
|
|
6528
|
+
if (entry.isDirectory()) await walk2(p);
|
|
6529
|
+
}
|
|
5514
6530
|
}
|
|
5515
|
-
|
|
6531
|
+
await walk2(dir);
|
|
6532
|
+
return out;
|
|
5516
6533
|
}
|
|
5517
|
-
function
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
6534
|
+
async function pruneEmptyParents(startDir, maxDepth) {
|
|
6535
|
+
let current = startDir;
|
|
6536
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
6537
|
+
try {
|
|
6538
|
+
const entries = await readdir4(current);
|
|
6539
|
+
if (entries.length > 0) return;
|
|
6540
|
+
await rm6(current, { recursive: false, force: true });
|
|
6541
|
+
current = dirname5(current);
|
|
6542
|
+
} catch {
|
|
6543
|
+
return;
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
function pushUnique(list, value) {
|
|
6548
|
+
if (!list.includes(value)) list.push(value);
|
|
6549
|
+
}
|
|
6550
|
+
async function writeTextFileAtomic(path4, content) {
|
|
6551
|
+
await mkdir5(dirname5(path4), { recursive: true });
|
|
6552
|
+
const tmp = `${path4}.${randomUUID3()}.tmp`;
|
|
6553
|
+
try {
|
|
6554
|
+
await writeFile5(tmp, content, "utf8");
|
|
6555
|
+
await rename3(tmp, path4);
|
|
6556
|
+
} catch (err) {
|
|
6557
|
+
await rm6(tmp, { force: true }).catch(() => {
|
|
6558
|
+
});
|
|
6559
|
+
throw err;
|
|
6560
|
+
}
|
|
5521
6561
|
}
|
|
5522
|
-
|
|
5523
|
-
// src/commands/plugin/install.ts
|
|
5524
|
-
import { rm as rm11 } from "fs/promises";
|
|
5525
|
-
import { homedir as homedir17 } from "os";
|
|
5526
|
-
import { resolve as resolve20 } from "path";
|
|
5527
6562
|
|
|
5528
6563
|
// src/installers/claude-code/installer.ts
|
|
5529
|
-
import { randomUUID as
|
|
6564
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
5530
6565
|
import {
|
|
5531
6566
|
copyFile,
|
|
5532
6567
|
lstat,
|
|
5533
|
-
mkdir as
|
|
5534
|
-
readdir as
|
|
5535
|
-
readFile as
|
|
6568
|
+
mkdir as mkdir6,
|
|
6569
|
+
readdir as readdir5,
|
|
6570
|
+
readFile as readFile7,
|
|
5536
6571
|
readlink,
|
|
5537
|
-
rename as
|
|
5538
|
-
rm as
|
|
5539
|
-
stat as
|
|
6572
|
+
rename as rename4,
|
|
6573
|
+
rm as rm7,
|
|
6574
|
+
stat as stat4,
|
|
5540
6575
|
symlink,
|
|
5541
6576
|
unlink
|
|
5542
6577
|
} from "fs/promises";
|
|
5543
|
-
import { homedir as
|
|
5544
|
-
import { dirname as
|
|
6578
|
+
import { homedir as homedir11 } from "os";
|
|
6579
|
+
import { dirname as dirname6, join as join10, resolve as resolve13 } from "path";
|
|
5545
6580
|
|
|
5546
6581
|
// src/installers/claude-code/paths.ts
|
|
5547
|
-
import { resolve as
|
|
6582
|
+
import { resolve as resolve12 } from "path";
|
|
5548
6583
|
var CLAUDE_DIR = ".claude";
|
|
5549
6584
|
var PLUGINS_SUBDIR = "plugins";
|
|
5550
6585
|
var CACHE_SUBDIR = "cache";
|
|
5551
|
-
var
|
|
6586
|
+
var PLUGIN_MANIFEST_RELATIVE2 = ".claude-plugin/plugin.json";
|
|
5552
6587
|
var CAPABILITY_DIRS = [
|
|
5553
6588
|
"commands",
|
|
5554
6589
|
"skills",
|
|
@@ -5562,27 +6597,27 @@ var ClaudeCodePaths = class {
|
|
|
5562
6597
|
}
|
|
5563
6598
|
/** `<home>/.claude/` */
|
|
5564
6599
|
get claudeDir() {
|
|
5565
|
-
return
|
|
6600
|
+
return resolve12(this.home, CLAUDE_DIR);
|
|
5566
6601
|
}
|
|
5567
6602
|
/** `<home>/.claude/settings.json` */
|
|
5568
6603
|
get settingsJson() {
|
|
5569
|
-
return
|
|
6604
|
+
return resolve12(this.claudeDir, "settings.json");
|
|
5570
6605
|
}
|
|
5571
6606
|
/** `<home>/.claude/plugins/` */
|
|
5572
6607
|
get pluginsDir() {
|
|
5573
|
-
return
|
|
6608
|
+
return resolve12(this.claudeDir, PLUGINS_SUBDIR);
|
|
5574
6609
|
}
|
|
5575
6610
|
/** `<home>/.claude/plugins/known_marketplaces.json` */
|
|
5576
6611
|
get knownMarketplacesJson() {
|
|
5577
|
-
return
|
|
6612
|
+
return resolve12(this.pluginsDir, "known_marketplaces.json");
|
|
5578
6613
|
}
|
|
5579
6614
|
/** `<home>/.claude/plugins/installed_plugins.json` */
|
|
5580
6615
|
get installedPluginsJson() {
|
|
5581
|
-
return
|
|
6616
|
+
return resolve12(this.pluginsDir, "installed_plugins.json");
|
|
5582
6617
|
}
|
|
5583
6618
|
/** `<home>/.claude/plugins/cache/` */
|
|
5584
6619
|
get cacheDir() {
|
|
5585
|
-
return
|
|
6620
|
+
return resolve12(this.pluginsDir, CACHE_SUBDIR);
|
|
5586
6621
|
}
|
|
5587
6622
|
/**
|
|
5588
6623
|
* `<home>/.claude/plugins/marketplaces/`。
|
|
@@ -5592,37 +6627,37 @@ var ClaudeCodePaths = class {
|
|
|
5592
6627
|
* Claude Code 会识别不到 plugin 条目(regression fix,bug #4)。
|
|
5593
6628
|
*/
|
|
5594
6629
|
get marketplacesRootDir() {
|
|
5595
|
-
return
|
|
6630
|
+
return resolve12(this.pluginsDir, "marketplaces");
|
|
5596
6631
|
}
|
|
5597
6632
|
/** `<home>/.claude/plugins/marketplaces/<mkt>/` */
|
|
5598
6633
|
marketplaceInstallDir(marketplace) {
|
|
5599
|
-
return
|
|
6634
|
+
return resolve12(this.marketplacesRootDir, marketplace);
|
|
5600
6635
|
}
|
|
5601
6636
|
/** `<home>/.claude/plugins/cache/<mkt>/` */
|
|
5602
6637
|
marketplaceCacheDir(marketplace) {
|
|
5603
|
-
return
|
|
6638
|
+
return resolve12(this.cacheDir, marketplace);
|
|
5604
6639
|
}
|
|
5605
6640
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/` */
|
|
5606
6641
|
pluginCacheDir(ref) {
|
|
5607
|
-
return
|
|
6642
|
+
return resolve12(this.marketplaceCacheDir(ref.marketplace), ref.name);
|
|
5608
6643
|
}
|
|
5609
6644
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/` */
|
|
5610
6645
|
pluginVersionDir(ref, version) {
|
|
5611
|
-
return
|
|
6646
|
+
return resolve12(this.pluginCacheDir(ref), version);
|
|
5612
6647
|
}
|
|
5613
6648
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/.claude-plugin/plugin.json` */
|
|
5614
6649
|
pluginManifestPath(ref, version) {
|
|
5615
|
-
return
|
|
6650
|
+
return resolve12(
|
|
5616
6651
|
this.pluginVersionDir(ref, version),
|
|
5617
|
-
|
|
6652
|
+
PLUGIN_MANIFEST_RELATIVE2
|
|
5618
6653
|
);
|
|
5619
6654
|
}
|
|
5620
6655
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/<capability>/` */
|
|
5621
6656
|
capabilityDir(ref, version, capability) {
|
|
5622
|
-
return
|
|
6657
|
+
return resolve12(this.pluginVersionDir(ref, version), capability);
|
|
5623
6658
|
}
|
|
5624
6659
|
};
|
|
5625
|
-
function
|
|
6660
|
+
function pluginKey2(ref) {
|
|
5626
6661
|
return `${ref.name}@${ref.marketplace}`;
|
|
5627
6662
|
}
|
|
5628
6663
|
|
|
@@ -5635,7 +6670,7 @@ var ClaudeCodeInstaller = class {
|
|
|
5635
6670
|
marketplaceSource;
|
|
5636
6671
|
symlinkRunner;
|
|
5637
6672
|
constructor(opts = {}) {
|
|
5638
|
-
const home = opts.home ??
|
|
6673
|
+
const home = opts.home ?? homedir11();
|
|
5639
6674
|
this.paths = new ClaudeCodePaths(home);
|
|
5640
6675
|
this.rushBinaryResolver = opts.rushBinaryResolver ?? defaultRushBinaryResolver;
|
|
5641
6676
|
this.now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -5658,7 +6693,7 @@ var ClaudeCodeInstaller = class {
|
|
|
5658
6693
|
this.paths.installedPluginsJson,
|
|
5659
6694
|
() => ({ version: 2, plugins: {} })
|
|
5660
6695
|
);
|
|
5661
|
-
const entries = data.plugins?.[
|
|
6696
|
+
const entries = data.plugins?.[pluginKey2(ref)];
|
|
5662
6697
|
return Array.isArray(entries) && entries.length > 0;
|
|
5663
6698
|
}
|
|
5664
6699
|
async list() {
|
|
@@ -5672,7 +6707,7 @@ var ClaudeCodeInstaller = class {
|
|
|
5672
6707
|
const out = [];
|
|
5673
6708
|
for (const [key, scopeEntries] of Object.entries(data.plugins ?? {})) {
|
|
5674
6709
|
if (!Array.isArray(scopeEntries) || scopeEntries.length === 0) continue;
|
|
5675
|
-
const ref =
|
|
6710
|
+
const ref = parsePluginKey3(key);
|
|
5676
6711
|
if (!ref) continue;
|
|
5677
6712
|
const pick = scopeEntries.find((e) => e.scope === "user") ?? scopeEntries[0];
|
|
5678
6713
|
if (!pick) continue;
|
|
@@ -5700,7 +6735,7 @@ var ClaudeCodeInstaller = class {
|
|
|
5700
6735
|
}
|
|
5701
6736
|
const { ref, version } = plugin;
|
|
5702
6737
|
const pluginVersionDir = this.paths.pluginVersionDir(ref, version);
|
|
5703
|
-
const key =
|
|
6738
|
+
const key = pluginKey2(ref);
|
|
5704
6739
|
if (!opts.force && version !== "unknown") {
|
|
5705
6740
|
let already = false;
|
|
5706
6741
|
try {
|
|
@@ -5733,13 +6768,13 @@ var ClaudeCodeInstaller = class {
|
|
|
5733
6768
|
}
|
|
5734
6769
|
try {
|
|
5735
6770
|
const writtenFiles = [];
|
|
5736
|
-
await
|
|
6771
|
+
await mkdir6(pluginVersionDir, { recursive: true });
|
|
5737
6772
|
writtenFiles.push(pluginVersionDir);
|
|
5738
6773
|
for (const cap of CAPABILITY_DIRS) {
|
|
5739
|
-
const srcDir =
|
|
6774
|
+
const srcDir = resolve13(plugin.sourceDir, cap);
|
|
5740
6775
|
if (!await dirExists(srcDir)) continue;
|
|
5741
6776
|
const dstDir = this.paths.capabilityDir(ref, version, cap);
|
|
5742
|
-
await
|
|
6777
|
+
await mkdir6(dstDir, { recursive: true });
|
|
5743
6778
|
writtenFiles.push(dstDir);
|
|
5744
6779
|
const copied = await copyDirTracked(srcDir, dstDir);
|
|
5745
6780
|
writtenFiles.push(...copied);
|
|
@@ -5751,11 +6786,11 @@ var ClaudeCodeInstaller = class {
|
|
|
5751
6786
|
this.rushBinaryResolver
|
|
5752
6787
|
);
|
|
5753
6788
|
if (normalizedMcp && Object.keys(normalizedMcp).length > 0) {
|
|
5754
|
-
const mcpJsonPath =
|
|
6789
|
+
const mcpJsonPath = resolve13(pluginVersionDir, ".mcp.json");
|
|
5755
6790
|
await writeJsonFile(mcpJsonPath, { mcpServers: normalizedMcp });
|
|
5756
6791
|
writtenFiles.push(mcpJsonPath);
|
|
5757
6792
|
}
|
|
5758
|
-
const pluginJsonContent =
|
|
6793
|
+
const pluginJsonContent = buildPluginJson2(plugin, normalizedMcp);
|
|
5759
6794
|
await writeJsonFile(manifestPath, pluginJsonContent);
|
|
5760
6795
|
writtenFiles.push(manifestPath);
|
|
5761
6796
|
const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];
|
|
@@ -5820,7 +6855,7 @@ var ClaudeCodeInstaller = class {
|
|
|
5820
6855
|
artifacts: { files: [], mcpKeys: [] }
|
|
5821
6856
|
};
|
|
5822
6857
|
}
|
|
5823
|
-
const key =
|
|
6858
|
+
const key = pluginKey2(ref);
|
|
5824
6859
|
const touchedFiles = [];
|
|
5825
6860
|
const installedMcpKeys = await this.collectMcpKeysFromInstalled(ref);
|
|
5826
6861
|
if (opts.dryRun) {
|
|
@@ -5879,8 +6914,8 @@ var ClaudeCodeInstaller = class {
|
|
|
5879
6914
|
await unlink(mktDir);
|
|
5880
6915
|
files.push(mktDir);
|
|
5881
6916
|
} else if (kind === "dir") {
|
|
5882
|
-
if (await pathExists(
|
|
5883
|
-
await
|
|
6917
|
+
if (await pathExists(join10(mktDir, ".rush-marketplace-source.json"))) {
|
|
6918
|
+
await rm7(mktDir, { recursive: true, force: true });
|
|
5884
6919
|
files.push(mktDir);
|
|
5885
6920
|
}
|
|
5886
6921
|
}
|
|
@@ -5951,12 +6986,12 @@ var ClaudeCodeInstaller = class {
|
|
|
5951
6986
|
return { written: false, path: "" };
|
|
5952
6987
|
}
|
|
5953
6988
|
const mktDir = this.paths.marketplaceInstallDir(marketplaceName);
|
|
5954
|
-
await
|
|
6989
|
+
await mkdir6(this.paths.marketplacesRootDir, { recursive: true });
|
|
5955
6990
|
const kind = await inodeKind(mktDir);
|
|
5956
6991
|
if (kind === "symlink") {
|
|
5957
6992
|
const target = await readlink(mktDir).catch(() => null);
|
|
5958
|
-
const resolvedTarget = target !== null ?
|
|
5959
|
-
if (resolvedTarget && resolvedTarget ===
|
|
6993
|
+
const resolvedTarget = target !== null ? resolve13(dirname6(mktDir), target) : null;
|
|
6994
|
+
if (resolvedTarget && resolvedTarget === resolve13(src.rootDir)) {
|
|
5960
6995
|
return { written: false, path: mktDir };
|
|
5961
6996
|
}
|
|
5962
6997
|
throw new Error(
|
|
@@ -5964,7 +6999,7 @@ var ClaudeCodeInstaller = class {
|
|
|
5964
6999
|
);
|
|
5965
7000
|
}
|
|
5966
7001
|
if (kind === "dir") {
|
|
5967
|
-
const metaPath =
|
|
7002
|
+
const metaPath = join10(mktDir, ".rush-marketplace-source.json");
|
|
5968
7003
|
if (await pathExists(metaPath)) {
|
|
5969
7004
|
const metaSourceRaw = await readMarketplaceCacheSource(metaPath);
|
|
5970
7005
|
const currentSourceRaw = descriptorToSourceRaw(src.descriptor);
|
|
@@ -5989,7 +7024,7 @@ var ClaudeCodeInstaller = class {
|
|
|
5989
7024
|
return { written: true, path: mktDir };
|
|
5990
7025
|
}
|
|
5991
7026
|
await copyDirTracked(src.rootDir, mktDir).catch(async (err) => {
|
|
5992
|
-
await
|
|
7027
|
+
await rm7(mktDir, { recursive: true, force: true }).catch(() => {
|
|
5993
7028
|
});
|
|
5994
7029
|
throw err;
|
|
5995
7030
|
});
|
|
@@ -6016,7 +7051,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6016
7051
|
this.paths.installedPluginsJson,
|
|
6017
7052
|
() => ({ version: 2, plugins: {} })
|
|
6018
7053
|
);
|
|
6019
|
-
const entries = data.plugins?.[
|
|
7054
|
+
const entries = data.plugins?.[pluginKey2(ref)];
|
|
6020
7055
|
if (!Array.isArray(entries)) return false;
|
|
6021
7056
|
return entries.some((e) => e.scope === "user" && e.version === version);
|
|
6022
7057
|
}
|
|
@@ -6025,11 +7060,11 @@ var ClaudeCodeInstaller = class {
|
|
|
6025
7060
|
const pluginVersionDir = this.paths.pluginVersionDir(ref, version);
|
|
6026
7061
|
const dryFiles = [pluginVersionDir];
|
|
6027
7062
|
for (const cap of CAPABILITY_DIRS) {
|
|
6028
|
-
const srcDir =
|
|
7063
|
+
const srcDir = resolve13(plugin.sourceDir, cap);
|
|
6029
7064
|
if (!await dirExists(srcDir)) continue;
|
|
6030
7065
|
const dstDir = this.paths.capabilityDir(ref, version, cap);
|
|
6031
7066
|
dryFiles.push(dstDir);
|
|
6032
|
-
const plannedFiles = await
|
|
7067
|
+
const plannedFiles = await listDirFilesRecursive2(srcDir, dstDir);
|
|
6033
7068
|
dryFiles.push(...plannedFiles);
|
|
6034
7069
|
}
|
|
6035
7070
|
dryFiles.push(this.paths.pluginManifestPath(ref, version));
|
|
@@ -6039,7 +7074,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6039
7074
|
this.rushBinaryResolver
|
|
6040
7075
|
);
|
|
6041
7076
|
if (normalizedMcpDry && Object.keys(normalizedMcpDry).length > 0) {
|
|
6042
|
-
dryFiles.push(
|
|
7077
|
+
dryFiles.push(resolve13(pluginVersionDir, ".mcp.json"));
|
|
6043
7078
|
}
|
|
6044
7079
|
if (this.marketplaceSource) {
|
|
6045
7080
|
const mktInstallDir = this.paths.marketplaceInstallDir(ref.marketplace);
|
|
@@ -6053,7 +7088,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6053
7088
|
if (await this.wouldInstalledPluginsChange(ref, version, pluginVersionDir)) {
|
|
6054
7089
|
dryFiles.push(this.paths.installedPluginsJson);
|
|
6055
7090
|
}
|
|
6056
|
-
if (await this.wouldSettingsChange(
|
|
7091
|
+
if (await this.wouldSettingsChange(pluginKey2(ref))) {
|
|
6057
7092
|
dryFiles.push(this.paths.settingsJson);
|
|
6058
7093
|
}
|
|
6059
7094
|
const normalizedMcp = await resolveClaudeMcpServers(
|
|
@@ -6088,7 +7123,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6088
7123
|
this.paths.installedPluginsJson,
|
|
6089
7124
|
() => ({ version: 2, plugins: {} })
|
|
6090
7125
|
);
|
|
6091
|
-
const key =
|
|
7126
|
+
const key = pluginKey2(ref);
|
|
6092
7127
|
const entries = data.plugins?.[key] ?? [];
|
|
6093
7128
|
const userEntry = entries.find((e) => e.scope === "user");
|
|
6094
7129
|
if (!userEntry) return true;
|
|
@@ -6153,8 +7188,8 @@ var ClaudeCodeInstaller = class {
|
|
|
6153
7188
|
const settings = await this.readRawJsonForRollback(this.paths.settingsJson);
|
|
6154
7189
|
let backupVersionDir = null;
|
|
6155
7190
|
if (preexistedVersion === "dir") {
|
|
6156
|
-
backupVersionDir = `${pluginVersionDir}.rollback-${
|
|
6157
|
-
await
|
|
7191
|
+
backupVersionDir = `${pluginVersionDir}.rollback-${randomUUID4()}`;
|
|
7192
|
+
await rename4(pluginVersionDir, backupVersionDir);
|
|
6158
7193
|
}
|
|
6159
7194
|
return {
|
|
6160
7195
|
ref,
|
|
@@ -6175,7 +7210,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6175
7210
|
*/
|
|
6176
7211
|
async finalizeRollback(snap) {
|
|
6177
7212
|
if (snap.backupVersionDir) {
|
|
6178
|
-
await
|
|
7213
|
+
await rm7(snap.backupVersionDir, { recursive: true, force: true });
|
|
6179
7214
|
}
|
|
6180
7215
|
}
|
|
6181
7216
|
/**
|
|
@@ -6188,19 +7223,19 @@ var ClaudeCodeInstaller = class {
|
|
|
6188
7223
|
*/
|
|
6189
7224
|
async applyRollback(snap) {
|
|
6190
7225
|
if (snap.preexistedVersion === "dir" && snap.backupVersionDir) {
|
|
6191
|
-
await
|
|
6192
|
-
await
|
|
7226
|
+
await rm7(snap.pluginVersionDir, { recursive: true, force: true });
|
|
7227
|
+
await rename4(snap.backupVersionDir, snap.pluginVersionDir);
|
|
6193
7228
|
} else if (snap.preexistedVersion === "missing") {
|
|
6194
|
-
await
|
|
7229
|
+
await rm7(snap.pluginVersionDir, { recursive: true, force: true });
|
|
6195
7230
|
}
|
|
6196
7231
|
if (snap.preexistedPlugin === "missing") {
|
|
6197
|
-
await
|
|
7232
|
+
await rm7(this.paths.pluginCacheDir(snap.ref), {
|
|
6198
7233
|
recursive: true,
|
|
6199
7234
|
force: true
|
|
6200
7235
|
});
|
|
6201
7236
|
}
|
|
6202
7237
|
if (snap.preexistedMarketplace === "missing") {
|
|
6203
|
-
await
|
|
7238
|
+
await rm7(this.paths.marketplaceCacheDir(snap.ref.marketplace), {
|
|
6204
7239
|
recursive: true,
|
|
6205
7240
|
force: true
|
|
6206
7241
|
});
|
|
@@ -6214,7 +7249,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6214
7249
|
await unlink(mktInstallDir).catch(() => {
|
|
6215
7250
|
});
|
|
6216
7251
|
} else if (currentKind !== "missing") {
|
|
6217
|
-
await
|
|
7252
|
+
await rm7(mktInstallDir, { recursive: true, force: true }).catch(
|
|
6218
7253
|
() => {
|
|
6219
7254
|
}
|
|
6220
7255
|
);
|
|
@@ -6237,7 +7272,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6237
7272
|
async restoreRawJson(path4, state) {
|
|
6238
7273
|
if (!state.existed) {
|
|
6239
7274
|
if (await pathExists(path4)) {
|
|
6240
|
-
await
|
|
7275
|
+
await rm7(path4, { force: true });
|
|
6241
7276
|
}
|
|
6242
7277
|
return;
|
|
6243
7278
|
}
|
|
@@ -6332,7 +7367,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6332
7367
|
this.paths.installedPluginsJson,
|
|
6333
7368
|
() => ({ version: 2, plugins: {} })
|
|
6334
7369
|
);
|
|
6335
|
-
const key =
|
|
7370
|
+
const key = pluginKey2(ref);
|
|
6336
7371
|
const now = this.now();
|
|
6337
7372
|
const existingEntries = data.plugins?.[key] ?? [];
|
|
6338
7373
|
const userIdx = existingEntries.findIndex((e) => e.scope === "user");
|
|
@@ -6406,7 +7441,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6406
7441
|
this.paths.installedPluginsJson,
|
|
6407
7442
|
() => ({ version: 2, plugins: {} })
|
|
6408
7443
|
);
|
|
6409
|
-
const key =
|
|
7444
|
+
const key = pluginKey2(ref);
|
|
6410
7445
|
const existingEntries = data.plugins?.[key];
|
|
6411
7446
|
if (!Array.isArray(existingEntries) || existingEntries.length === 0) {
|
|
6412
7447
|
return false;
|
|
@@ -6461,7 +7496,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6461
7496
|
this.paths.installedPluginsJson,
|
|
6462
7497
|
() => ({ version: 2, plugins: {} })
|
|
6463
7498
|
);
|
|
6464
|
-
const entries = data.plugins?.[
|
|
7499
|
+
const entries = data.plugins?.[pluginKey2(ref)];
|
|
6465
7500
|
if (!Array.isArray(entries) || entries.length === 0) return false;
|
|
6466
7501
|
return entries.some((e) => e.scope === "user");
|
|
6467
7502
|
}
|
|
@@ -6489,7 +7524,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6489
7524
|
);
|
|
6490
7525
|
const servers = data.mcpServers;
|
|
6491
7526
|
if (typeof servers === "string") {
|
|
6492
|
-
const mcpJsonPath =
|
|
7527
|
+
const mcpJsonPath = resolve13(dirname6(manifestPath), "..", servers);
|
|
6493
7528
|
if (!await pathExists(mcpJsonPath)) return [];
|
|
6494
7529
|
const { data: mcpData } = await readJsonFile(
|
|
6495
7530
|
mcpJsonPath,
|
|
@@ -6514,11 +7549,11 @@ var ClaudeCodeInstaller = class {
|
|
|
6514
7549
|
this.paths.installedPluginsJson,
|
|
6515
7550
|
() => ({ version: 2, plugins: {} })
|
|
6516
7551
|
);
|
|
6517
|
-
const entries = data.plugins?.[
|
|
7552
|
+
const entries = data.plugins?.[pluginKey2(ref)];
|
|
6518
7553
|
if (!Array.isArray(entries) || entries.length === 0) return null;
|
|
6519
7554
|
const userEntry = entries.find((e) => e.scope === "user") ?? entries[0];
|
|
6520
7555
|
if (!userEntry) return null;
|
|
6521
|
-
const manifestPath =
|
|
7556
|
+
const manifestPath = join10(
|
|
6522
7557
|
userEntry.installPath,
|
|
6523
7558
|
".claude-plugin",
|
|
6524
7559
|
"plugin.json"
|
|
@@ -6529,7 +7564,7 @@ var ClaudeCodeInstaller = class {
|
|
|
6529
7564
|
return null;
|
|
6530
7565
|
}
|
|
6531
7566
|
};
|
|
6532
|
-
function
|
|
7567
|
+
function parsePluginKey3(key) {
|
|
6533
7568
|
const at = key.indexOf("@");
|
|
6534
7569
|
if (at <= 0 || at === key.length - 1) return null;
|
|
6535
7570
|
return { name: key.slice(0, at), marketplace: key.slice(at + 1) };
|
|
@@ -6551,7 +7586,7 @@ async function resolveClaudeMcpServers(ref, plugin, rushBinaryResolver) {
|
|
|
6551
7586
|
}
|
|
6552
7587
|
return void 0;
|
|
6553
7588
|
}
|
|
6554
|
-
function
|
|
7589
|
+
function buildPluginJson2(plugin, mcpServers) {
|
|
6555
7590
|
const { manifest } = plugin;
|
|
6556
7591
|
const {
|
|
6557
7592
|
mcpServers: _origMcp,
|
|
@@ -6567,7 +7602,7 @@ function buildPluginJson(plugin, mcpServers) {
|
|
|
6567
7602
|
}
|
|
6568
7603
|
async function readMarketplaceCacheSource(metaPath) {
|
|
6569
7604
|
try {
|
|
6570
|
-
const raw = await
|
|
7605
|
+
const raw = await readFile7(metaPath, "utf8");
|
|
6571
7606
|
const parsed = JSON.parse(raw);
|
|
6572
7607
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
6573
7608
|
const sourceRaw = parsed.sourceRaw;
|
|
@@ -6602,7 +7637,7 @@ async function defaultSymlinkRunner(target, linkPath) {
|
|
|
6602
7637
|
}
|
|
6603
7638
|
async function dirExists(p) {
|
|
6604
7639
|
try {
|
|
6605
|
-
const s = await
|
|
7640
|
+
const s = await stat4(p);
|
|
6606
7641
|
return s.isDirectory();
|
|
6607
7642
|
} catch {
|
|
6608
7643
|
return false;
|
|
@@ -6619,15 +7654,15 @@ async function inodeKind(p) {
|
|
|
6619
7654
|
return "missing";
|
|
6620
7655
|
}
|
|
6621
7656
|
}
|
|
6622
|
-
async function
|
|
7657
|
+
async function listDirFilesRecursive2(srcDir, dstDir) {
|
|
6623
7658
|
const planned = [];
|
|
6624
|
-
const entries = await
|
|
7659
|
+
const entries = await readdir5(srcDir, { withFileTypes: true });
|
|
6625
7660
|
for (const entry of entries) {
|
|
6626
|
-
const srcPath =
|
|
6627
|
-
const dstPath =
|
|
7661
|
+
const srcPath = join10(srcDir, entry.name);
|
|
7662
|
+
const dstPath = join10(dstDir, entry.name);
|
|
6628
7663
|
if (entry.isDirectory()) {
|
|
6629
7664
|
planned.push(dstPath);
|
|
6630
|
-
const sub = await
|
|
7665
|
+
const sub = await listDirFilesRecursive2(srcPath, dstPath);
|
|
6631
7666
|
planned.push(...sub);
|
|
6632
7667
|
} else if (entry.isFile()) {
|
|
6633
7668
|
planned.push(dstPath);
|
|
@@ -6637,17 +7672,17 @@ async function listDirFilesRecursive(srcDir, dstDir) {
|
|
|
6637
7672
|
}
|
|
6638
7673
|
async function copyDirTracked(srcDir, dstDir) {
|
|
6639
7674
|
const written = [];
|
|
6640
|
-
const entries = await
|
|
7675
|
+
const entries = await readdir5(srcDir, { withFileTypes: true });
|
|
6641
7676
|
for (const entry of entries) {
|
|
6642
|
-
const srcPath =
|
|
6643
|
-
const dstPath =
|
|
7677
|
+
const srcPath = join10(srcDir, entry.name);
|
|
7678
|
+
const dstPath = join10(dstDir, entry.name);
|
|
6644
7679
|
if (entry.isDirectory()) {
|
|
6645
|
-
await
|
|
7680
|
+
await mkdir6(dstPath, { recursive: true });
|
|
6646
7681
|
written.push(dstPath);
|
|
6647
7682
|
const subWritten = await copyDirTracked(srcPath, dstPath);
|
|
6648
7683
|
written.push(...subWritten);
|
|
6649
7684
|
} else if (entry.isFile()) {
|
|
6650
|
-
await
|
|
7685
|
+
await mkdir6(dirname6(dstPath), { recursive: true });
|
|
6651
7686
|
await copyFile(srcPath, dstPath);
|
|
6652
7687
|
written.push(dstPath);
|
|
6653
7688
|
}
|
|
@@ -6658,7 +7693,7 @@ async function writeAtomicWithMtimeCheck(filePath, data, expectedMtimeMs) {
|
|
|
6658
7693
|
if (expectedMtimeMs !== null) {
|
|
6659
7694
|
let currentMtime = null;
|
|
6660
7695
|
try {
|
|
6661
|
-
const s = await
|
|
7696
|
+
const s = await stat4(filePath);
|
|
6662
7697
|
currentMtime = s.mtimeMs;
|
|
6663
7698
|
} catch {
|
|
6664
7699
|
currentMtime = null;
|
|
@@ -6681,20 +7716,20 @@ async function retryOnConflict(fn) {
|
|
|
6681
7716
|
}
|
|
6682
7717
|
|
|
6683
7718
|
// src/installers/cursor/installer.ts
|
|
6684
|
-
import { randomUUID as
|
|
7719
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
6685
7720
|
import { constants as fsConstants7 } from "fs";
|
|
6686
7721
|
import {
|
|
6687
7722
|
access as access7,
|
|
6688
|
-
cp as
|
|
6689
|
-
mkdir as
|
|
6690
|
-
readFile as
|
|
6691
|
-
rename as
|
|
6692
|
-
rm as
|
|
6693
|
-
stat as
|
|
6694
|
-
writeFile as
|
|
7723
|
+
cp as cp3,
|
|
7724
|
+
mkdir as mkdir8,
|
|
7725
|
+
readFile as readFile10,
|
|
7726
|
+
rename as rename6,
|
|
7727
|
+
rm as rm9,
|
|
7728
|
+
stat as stat6,
|
|
7729
|
+
writeFile as writeFile7
|
|
6695
7730
|
} from "fs/promises";
|
|
6696
|
-
import { homedir as
|
|
6697
|
-
import { dirname as
|
|
7731
|
+
import { homedir as homedir12 } from "os";
|
|
7732
|
+
import { dirname as dirname8, relative, resolve as resolve16 } from "path";
|
|
6698
7733
|
|
|
6699
7734
|
// src/installers/cursor/marker.ts
|
|
6700
7735
|
var RUSH_AI_MARKER = "<!-- rush-ai:auto-generated -->";
|
|
@@ -6703,18 +7738,18 @@ function hasRushAiMarker(fileContent) {
|
|
|
6703
7738
|
}
|
|
6704
7739
|
|
|
6705
7740
|
// src/installers/cursor/mcp.ts
|
|
6706
|
-
import { randomUUID as
|
|
7741
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
6707
7742
|
import { constants as fsConstants6 } from "fs";
|
|
6708
7743
|
import {
|
|
6709
7744
|
access as access6,
|
|
6710
|
-
mkdir as
|
|
6711
|
-
readFile as
|
|
6712
|
-
rename as
|
|
6713
|
-
rm as
|
|
6714
|
-
stat as
|
|
6715
|
-
writeFile as
|
|
7745
|
+
mkdir as mkdir7,
|
|
7746
|
+
readFile as readFile8,
|
|
7747
|
+
rename as rename5,
|
|
7748
|
+
rm as rm8,
|
|
7749
|
+
stat as stat5,
|
|
7750
|
+
writeFile as writeFile6
|
|
6716
7751
|
} from "fs/promises";
|
|
6717
|
-
import { dirname as
|
|
7752
|
+
import { dirname as dirname7 } from "path";
|
|
6718
7753
|
async function readCursorMcpJson(filePath) {
|
|
6719
7754
|
return (await readCursorMcpJsonWithMtime(filePath)).data;
|
|
6720
7755
|
}
|
|
@@ -6722,8 +7757,8 @@ async function readCursorMcpJsonWithMtime(filePath) {
|
|
|
6722
7757
|
if (!await pathExists5(filePath)) {
|
|
6723
7758
|
return { data: {}, mtimeMs: null };
|
|
6724
7759
|
}
|
|
6725
|
-
const stats = await
|
|
6726
|
-
const raw = await
|
|
7760
|
+
const stats = await stat5(filePath);
|
|
7761
|
+
const raw = await readFile8(filePath, "utf8");
|
|
6727
7762
|
let parsed;
|
|
6728
7763
|
try {
|
|
6729
7764
|
parsed = JSON.parse(raw);
|
|
@@ -6752,10 +7787,16 @@ function mergeMcpServers(current, additions) {
|
|
|
6752
7787
|
cloned.mcpServers = {};
|
|
6753
7788
|
}
|
|
6754
7789
|
for (const [key, config] of Object.entries(additions)) {
|
|
6755
|
-
cloned.mcpServers[key] =
|
|
7790
|
+
cloned.mcpServers[key] = toCursorMcpEntry(config);
|
|
6756
7791
|
}
|
|
6757
7792
|
return cloned;
|
|
6758
7793
|
}
|
|
7794
|
+
function toCursorMcpEntry(config) {
|
|
7795
|
+
if (typeof config.command === "string") {
|
|
7796
|
+
return toCursorStdioEntry({ ...config, command: config.command });
|
|
7797
|
+
}
|
|
7798
|
+
return toCursorRemoteOrUnknownEntry(config);
|
|
7799
|
+
}
|
|
6759
7800
|
function toCursorStdioEntry(config) {
|
|
6760
7801
|
const entry = {
|
|
6761
7802
|
type: "stdio",
|
|
@@ -6764,7 +7805,7 @@ function toCursorStdioEntry(config) {
|
|
|
6764
7805
|
if (config.args !== void 0) entry.args = [...config.args];
|
|
6765
7806
|
if (config.env !== void 0) entry.env = { ...config.env };
|
|
6766
7807
|
if (config.cwd !== void 0) entry.cwd = config.cwd;
|
|
6767
|
-
const handledKeys = /* @__PURE__ */ new Set(["command", "args", "env", "cwd"]);
|
|
7808
|
+
const handledKeys = /* @__PURE__ */ new Set(["type", "command", "args", "env", "cwd"]);
|
|
6768
7809
|
const raw = config;
|
|
6769
7810
|
for (const [key, value] of Object.entries(raw)) {
|
|
6770
7811
|
if (handledKeys.has(key)) continue;
|
|
@@ -6773,6 +7814,18 @@ function toCursorStdioEntry(config) {
|
|
|
6773
7814
|
}
|
|
6774
7815
|
return entry;
|
|
6775
7816
|
}
|
|
7817
|
+
function toCursorRemoteOrUnknownEntry(config) {
|
|
7818
|
+
const entry = {};
|
|
7819
|
+
const raw = config;
|
|
7820
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
7821
|
+
if (value === void 0) continue;
|
|
7822
|
+
entry[key] = deepCopyPrimitive(value);
|
|
7823
|
+
}
|
|
7824
|
+
if (typeof config.url === "string" && config.url.length > 0 && typeof config.type !== "string") {
|
|
7825
|
+
entry.type = "http";
|
|
7826
|
+
}
|
|
7827
|
+
return entry;
|
|
7828
|
+
}
|
|
6776
7829
|
function deepCopyPrimitive(v) {
|
|
6777
7830
|
if (v === null || typeof v !== "object") return v;
|
|
6778
7831
|
try {
|
|
@@ -6790,15 +7843,15 @@ function removeMcpServers(current, keysToRemove) {
|
|
|
6790
7843
|
return cloned;
|
|
6791
7844
|
}
|
|
6792
7845
|
async function writeCursorMcpJson(filePath, data) {
|
|
6793
|
-
await
|
|
7846
|
+
await mkdir7(dirname7(filePath), { recursive: true });
|
|
6794
7847
|
const content = `${JSON.stringify(data, null, 2)}
|
|
6795
7848
|
`;
|
|
6796
|
-
const tmp = `${filePath}.${
|
|
7849
|
+
const tmp = `${filePath}.${randomUUID5()}.tmp`;
|
|
6797
7850
|
try {
|
|
6798
|
-
await
|
|
6799
|
-
await
|
|
7851
|
+
await writeFile6(tmp, content, { encoding: "utf8", flag: "w" });
|
|
7852
|
+
await rename5(tmp, filePath);
|
|
6800
7853
|
} catch (err) {
|
|
6801
|
-
await
|
|
7854
|
+
await rm8(tmp, { force: true }).catch(() => {
|
|
6802
7855
|
});
|
|
6803
7856
|
throw err;
|
|
6804
7857
|
}
|
|
@@ -6813,25 +7866,25 @@ async function pathExists5(p) {
|
|
|
6813
7866
|
}
|
|
6814
7867
|
|
|
6815
7868
|
// src/installers/cursor/paths.ts
|
|
6816
|
-
import { resolve as
|
|
7869
|
+
import { resolve as resolve14 } from "path";
|
|
6817
7870
|
var CURSOR_RELATIVE_DIR = ".cursor";
|
|
6818
7871
|
function cursorDir(home) {
|
|
6819
|
-
return
|
|
7872
|
+
return resolve14(home, CURSOR_RELATIVE_DIR);
|
|
6820
7873
|
}
|
|
6821
7874
|
function cursorMcpJsonPath(home) {
|
|
6822
|
-
return
|
|
7875
|
+
return resolve14(cursorDir(home), "mcp.json");
|
|
6823
7876
|
}
|
|
6824
7877
|
function cursorRulesDir(home) {
|
|
6825
|
-
return
|
|
7878
|
+
return resolve14(cursorDir(home), "rules");
|
|
6826
7879
|
}
|
|
6827
7880
|
function cursorRuleMdcPath(home, ruleName) {
|
|
6828
|
-
return
|
|
7881
|
+
return resolve14(cursorRulesDir(home), `${ruleName}.mdc`);
|
|
6829
7882
|
}
|
|
6830
7883
|
function cursorSkillsDir(home) {
|
|
6831
|
-
return
|
|
7884
|
+
return resolve14(cursorDir(home), "skills");
|
|
6832
7885
|
}
|
|
6833
7886
|
function cursorSkillDir(home, skillName) {
|
|
6834
|
-
return
|
|
7887
|
+
return resolve14(cursorSkillsDir(home), skillName);
|
|
6835
7888
|
}
|
|
6836
7889
|
function bridgeSkillFileReference(skillName) {
|
|
6837
7890
|
return `.cursor/skills/${skillName}/SKILL.md`;
|
|
@@ -6941,13 +7994,13 @@ function quoteIfNeeded(value) {
|
|
|
6941
7994
|
}
|
|
6942
7995
|
|
|
6943
7996
|
// src/installers/cursor/skills.ts
|
|
6944
|
-
import { readFile as
|
|
6945
|
-
import { resolve as
|
|
7997
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
7998
|
+
import { resolve as resolve15 } from "path";
|
|
6946
7999
|
async function readSkillDescription(skillSourceDir) {
|
|
6947
|
-
const skillMdPath =
|
|
8000
|
+
const skillMdPath = resolve15(skillSourceDir, "SKILL.md");
|
|
6948
8001
|
let raw;
|
|
6949
8002
|
try {
|
|
6950
|
-
raw = await
|
|
8003
|
+
raw = await readFile9(skillMdPath, "utf8");
|
|
6951
8004
|
} catch {
|
|
6952
8005
|
return void 0;
|
|
6953
8006
|
}
|
|
@@ -6977,11 +8030,11 @@ async function executeRollback(ledger) {
|
|
|
6977
8030
|
if (entry.kind === "restore-file") {
|
|
6978
8031
|
await atomicWriteFile(entry.path, entry.originalContent);
|
|
6979
8032
|
} else if (entry.kind === "remove-file") {
|
|
6980
|
-
await
|
|
8033
|
+
await rm9(entry.path, { force: true });
|
|
6981
8034
|
} else if (entry.kind === "remove-dir") {
|
|
6982
|
-
await
|
|
8035
|
+
await rm9(entry.path, { recursive: true, force: true });
|
|
6983
8036
|
} else {
|
|
6984
|
-
await
|
|
8037
|
+
await rm9(entry.path, { recursive: true, force: true });
|
|
6985
8038
|
await safeRename(entry.backupPath, entry.path);
|
|
6986
8039
|
}
|
|
6987
8040
|
} catch (err) {
|
|
@@ -6993,20 +8046,20 @@ async function executeRollback(ledger) {
|
|
|
6993
8046
|
return errors;
|
|
6994
8047
|
}
|
|
6995
8048
|
async function atomicWriteFile(filePath, content) {
|
|
6996
|
-
await
|
|
6997
|
-
const tmp = `${filePath}.${
|
|
8049
|
+
await mkdir8(dirname8(filePath), { recursive: true });
|
|
8050
|
+
const tmp = `${filePath}.${randomUUID6()}.tmp`;
|
|
6998
8051
|
try {
|
|
6999
|
-
await
|
|
7000
|
-
await
|
|
8052
|
+
await writeFile7(tmp, content, { encoding: "utf8", flag: "w" });
|
|
8053
|
+
await rename6(tmp, filePath);
|
|
7001
8054
|
} catch (err) {
|
|
7002
|
-
await
|
|
8055
|
+
await rm9(tmp, { force: true }).catch(() => {
|
|
7003
8056
|
});
|
|
7004
8057
|
throw err;
|
|
7005
8058
|
}
|
|
7006
8059
|
}
|
|
7007
8060
|
async function getMcpMtimeMs(filePath) {
|
|
7008
8061
|
try {
|
|
7009
|
-
const s = await
|
|
8062
|
+
const s = await stat6(filePath);
|
|
7010
8063
|
return s.mtimeMs;
|
|
7011
8064
|
} catch {
|
|
7012
8065
|
return null;
|
|
@@ -7039,7 +8092,7 @@ var CursorInstaller = class {
|
|
|
7039
8092
|
target = "cursor";
|
|
7040
8093
|
home;
|
|
7041
8094
|
constructor(options = {}) {
|
|
7042
|
-
this.home = options.home ??
|
|
8095
|
+
this.home = options.home ?? homedir12();
|
|
7043
8096
|
}
|
|
7044
8097
|
// -------------------------------------------------------------------------
|
|
7045
8098
|
// PluginInstaller 接口
|
|
@@ -7239,13 +8292,13 @@ var CursorInstaller = class {
|
|
|
7239
8292
|
if (info === "missing") continue;
|
|
7240
8293
|
if (info === "skill-dir") {
|
|
7241
8294
|
if (!dryRun) {
|
|
7242
|
-
await
|
|
8295
|
+
await rm9(filePath, { recursive: true, force: true });
|
|
7243
8296
|
}
|
|
7244
8297
|
removedFiles.push(filePath);
|
|
7245
8298
|
continue;
|
|
7246
8299
|
}
|
|
7247
8300
|
if (info === "mdc-file") {
|
|
7248
|
-
const content = await
|
|
8301
|
+
const content = await readFile10(filePath, "utf8").catch(() => "");
|
|
7249
8302
|
if (!hasRushAiMarker(content)) {
|
|
7250
8303
|
warnings.push(
|
|
7251
8304
|
`${filePath} \u5DF2\u88AB\u4FEE\u6539\uFF08marker \u4E22\u5931\uFF09\uFF0C\u8DF3\u8FC7\u4E0D\u52A8\u2014\u2014\u8BF7\u7528\u6237\u624B\u52A8\u5904\u7406`
|
|
@@ -7253,13 +8306,13 @@ var CursorInstaller = class {
|
|
|
7253
8306
|
continue;
|
|
7254
8307
|
}
|
|
7255
8308
|
if (!dryRun) {
|
|
7256
|
-
await
|
|
8309
|
+
await rm9(filePath, { force: true });
|
|
7257
8310
|
}
|
|
7258
8311
|
removedFiles.push(filePath);
|
|
7259
8312
|
continue;
|
|
7260
8313
|
}
|
|
7261
8314
|
if (!dryRun) {
|
|
7262
|
-
await
|
|
8315
|
+
await rm9(filePath, { force: true });
|
|
7263
8316
|
}
|
|
7264
8317
|
removedFiles.push(filePath);
|
|
7265
8318
|
}
|
|
@@ -7307,10 +8360,10 @@ var CursorInstaller = class {
|
|
|
7307
8360
|
const prevEntry = registryStore.get(plugin.ref);
|
|
7308
8361
|
const prevFiles = new Set(prevEntry?.targets?.cursor?.files ?? []);
|
|
7309
8362
|
if (plugin.capabilities.includes("skills")) {
|
|
7310
|
-
const skillsSourceDir =
|
|
8363
|
+
const skillsSourceDir = resolve16(plugin.sourceDir, "skills");
|
|
7311
8364
|
const skillDirs = await listSkillDirs(skillsSourceDir);
|
|
7312
8365
|
for (const skillName of skillDirs) {
|
|
7313
|
-
const srcDir =
|
|
8366
|
+
const srcDir = resolve16(skillsSourceDir, skillName);
|
|
7314
8367
|
const destDir = cursorSkillDir(this.home, skillName);
|
|
7315
8368
|
const bridgeRulePath = cursorRuleMdcPath(this.home, skillName);
|
|
7316
8369
|
if (await dirExists2(destDir)) {
|
|
@@ -7342,10 +8395,10 @@ var CursorInstaller = class {
|
|
|
7342
8395
|
}
|
|
7343
8396
|
}
|
|
7344
8397
|
if (plugin.capabilities.includes("rules")) {
|
|
7345
|
-
const rulesSourceDir =
|
|
8398
|
+
const rulesSourceDir = resolve16(plugin.sourceDir, "rules");
|
|
7346
8399
|
const rulesFiles = await listRuleMdFiles(rulesSourceDir);
|
|
7347
8400
|
for (const ruleFile of rulesFiles) {
|
|
7348
|
-
const srcPath =
|
|
8401
|
+
const srcPath = resolve16(rulesSourceDir, ruleFile);
|
|
7349
8402
|
const ruleName = ruleFile.replace(/\.md$/i, "");
|
|
7350
8403
|
const destPath = cursorRuleMdcPath(this.home, ruleName);
|
|
7351
8404
|
await assertMdcWritable(destPath, args.force);
|
|
@@ -7354,7 +8407,7 @@ var CursorInstaller = class {
|
|
|
7354
8407
|
`rule "${ruleName}" \u4E0E skill "${ruleName}" \u7684 bridge rule \u540C\u540D\uFF0Crule \u5185\u5BB9\u4F1A\u8986\u76D6 bridge rule`
|
|
7355
8408
|
);
|
|
7356
8409
|
}
|
|
7357
|
-
const src = await
|
|
8410
|
+
const src = await readFile10(srcPath, "utf8");
|
|
7358
8411
|
const parsed = parseMarkdown(src);
|
|
7359
8412
|
const content = renderMdc(parsed, {
|
|
7360
8413
|
descriptionFallback: plugin.manifest.description ?? ruleName
|
|
@@ -7382,7 +8435,7 @@ var CursorInstaller = class {
|
|
|
7382
8435
|
// -------------------------------------------------------------------------
|
|
7383
8436
|
async writeSkillDir(args) {
|
|
7384
8437
|
const preExisted = await dirExists2(args.destDir);
|
|
7385
|
-
await
|
|
8438
|
+
await mkdir8(dirname8(args.destDir), { recursive: true });
|
|
7386
8439
|
if (preExisted) {
|
|
7387
8440
|
const backup = `${args.destDir}.${process.pid}.${Date.now()}.bak`;
|
|
7388
8441
|
await safeRename(args.destDir, backup);
|
|
@@ -7394,12 +8447,12 @@ var CursorInstaller = class {
|
|
|
7394
8447
|
} else {
|
|
7395
8448
|
args.ledger.push({ kind: "remove-dir", path: args.destDir });
|
|
7396
8449
|
}
|
|
7397
|
-
await
|
|
8450
|
+
await cp3(args.srcDir, args.destDir, { recursive: true });
|
|
7398
8451
|
}
|
|
7399
8452
|
async writeMdcFile(args) {
|
|
7400
8453
|
const preExisted = await fileExists(args.path);
|
|
7401
8454
|
if (preExisted) {
|
|
7402
|
-
const original = await
|
|
8455
|
+
const original = await readFile10(args.path, "utf8");
|
|
7403
8456
|
args.ledger.push({
|
|
7404
8457
|
kind: "restore-file",
|
|
7405
8458
|
path: args.path,
|
|
@@ -7413,7 +8466,7 @@ var CursorInstaller = class {
|
|
|
7413
8466
|
async writeMcpMerge(args) {
|
|
7414
8467
|
const preExisted = await fileExists(args.mcpPath);
|
|
7415
8468
|
if (preExisted) {
|
|
7416
|
-
const original = await
|
|
8469
|
+
const original = await readFile10(args.mcpPath, "utf8");
|
|
7417
8470
|
args.ledger.push({
|
|
7418
8471
|
kind: "restore-file",
|
|
7419
8472
|
path: args.mcpPath,
|
|
@@ -7497,7 +8550,7 @@ function skippedResult() {
|
|
|
7497
8550
|
}
|
|
7498
8551
|
async function assertMdcWritable(path4, force) {
|
|
7499
8552
|
if (!await fileExists(path4)) return;
|
|
7500
|
-
const raw = await
|
|
8553
|
+
const raw = await readFile10(path4, "utf8").catch(() => "");
|
|
7501
8554
|
if (hasRushAiMarker(raw)) return;
|
|
7502
8555
|
if (force) return;
|
|
7503
8556
|
throw new CursorInstallConflictError(
|
|
@@ -7512,7 +8565,7 @@ async function extractMcpAdditions(plugin) {
|
|
|
7512
8565
|
if (typeof m === "object") {
|
|
7513
8566
|
serverMap = m;
|
|
7514
8567
|
} else if (typeof m === "string") {
|
|
7515
|
-
const { readExternalMcpServers: readExternalMcpServers2 } = await import("./mcp-
|
|
8568
|
+
const { readExternalMcpServers: readExternalMcpServers2 } = await import("./mcp-TROKQRBF.js");
|
|
7516
8569
|
serverMap = await readExternalMcpServers2(plugin.sourceDir, m);
|
|
7517
8570
|
}
|
|
7518
8571
|
if (!serverMap) return null;
|
|
@@ -7526,19 +8579,19 @@ async function extractMcpAdditions(plugin) {
|
|
|
7526
8579
|
}
|
|
7527
8580
|
async function listSkillDirs(skillsDir) {
|
|
7528
8581
|
if (!await dirExists2(skillsDir)) return [];
|
|
7529
|
-
const { readdir:
|
|
7530
|
-
const entries = await
|
|
8582
|
+
const { readdir: readdir7 } = await import("fs/promises");
|
|
8583
|
+
const entries = await readdir7(skillsDir, { withFileTypes: true });
|
|
7531
8584
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).filter((name) => !name.startsWith(".")).sort();
|
|
7532
8585
|
}
|
|
7533
8586
|
async function listRuleMdFiles(rulesDir) {
|
|
7534
8587
|
if (!await dirExists2(rulesDir)) return [];
|
|
7535
|
-
const { readdir:
|
|
7536
|
-
const entries = await
|
|
8588
|
+
const { readdir: readdir7 } = await import("fs/promises");
|
|
8589
|
+
const entries = await readdir7(rulesDir, { withFileTypes: true });
|
|
7537
8590
|
return entries.filter((e) => e.isFile() && /\.md$/i.test(e.name)).map((e) => e.name).sort();
|
|
7538
8591
|
}
|
|
7539
8592
|
async function dirExists2(p) {
|
|
7540
8593
|
try {
|
|
7541
|
-
const s = await
|
|
8594
|
+
const s = await stat6(p);
|
|
7542
8595
|
return s.isDirectory();
|
|
7543
8596
|
} catch {
|
|
7544
8597
|
return false;
|
|
@@ -7547,20 +8600,20 @@ async function dirExists2(p) {
|
|
|
7547
8600
|
async function fileExists(p) {
|
|
7548
8601
|
try {
|
|
7549
8602
|
await access7(p, fsConstants7.F_OK);
|
|
7550
|
-
const s = await
|
|
8603
|
+
const s = await stat6(p);
|
|
7551
8604
|
return s.isFile();
|
|
7552
8605
|
} catch {
|
|
7553
8606
|
return false;
|
|
7554
8607
|
}
|
|
7555
8608
|
}
|
|
7556
8609
|
async function safeRename(from, to) {
|
|
7557
|
-
const { rename:
|
|
7558
|
-
await
|
|
8610
|
+
const { rename: rename8 } = await import("fs/promises");
|
|
8611
|
+
await rename8(from, to);
|
|
7559
8612
|
}
|
|
7560
8613
|
async function classifyArtifactPath(p) {
|
|
7561
8614
|
let s;
|
|
7562
8615
|
try {
|
|
7563
|
-
s = await
|
|
8616
|
+
s = await stat6(p);
|
|
7564
8617
|
} catch {
|
|
7565
8618
|
return "missing";
|
|
7566
8619
|
}
|
|
@@ -7573,14 +8626,14 @@ async function classifyArtifactPath(p) {
|
|
|
7573
8626
|
}
|
|
7574
8627
|
|
|
7575
8628
|
// src/migration/cleanup.ts
|
|
7576
|
-
import { lstat as lstat3, rm as
|
|
7577
|
-
import { homedir as
|
|
7578
|
-
import { resolve as
|
|
8629
|
+
import { lstat as lstat3, rm as rm10, unlink as unlink2 } from "fs/promises";
|
|
8630
|
+
import { homedir as homedir14 } from "os";
|
|
8631
|
+
import { resolve as resolve18 } from "path";
|
|
7579
8632
|
|
|
7580
8633
|
// src/migration/detect.ts
|
|
7581
|
-
import { access as access8, lstat as lstat2, readFile as
|
|
7582
|
-
import { homedir as
|
|
7583
|
-
import { isAbsolute as isAbsolute3, resolve as
|
|
8634
|
+
import { access as access8, lstat as lstat2, readFile as readFile11, readlink as readlink2, stat as stat7 } from "fs/promises";
|
|
8635
|
+
import { homedir as homedir13 } from "os";
|
|
8636
|
+
import { isAbsolute as isAbsolute3, resolve as resolve17 } from "path";
|
|
7584
8637
|
var LEGACY_ASSET_MANIFEST_RELATIVE = ".rush/plugins/claude-code/asset-manifest.json";
|
|
7585
8638
|
var LEGACY_ASSETS_DIR_RELATIVE = ".rush/plugins/claude-code/assets";
|
|
7586
8639
|
var LEGACY_CLAUDE_CODE_DIR_RELATIVE = ".rush/plugins/claude-code";
|
|
@@ -7599,7 +8652,7 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
|
|
|
7599
8652
|
const lst = await lstat2(linkPath);
|
|
7600
8653
|
if (!lst.isSymbolicLink()) return false;
|
|
7601
8654
|
const rawTarget = await readlink2(linkPath);
|
|
7602
|
-
const resolvedTarget = isAbsolute3(rawTarget) ? rawTarget :
|
|
8655
|
+
const resolvedTarget = isAbsolute3(rawTarget) ? rawTarget : resolve17(linkPath, "..", rawTarget);
|
|
7603
8656
|
return resolvedTarget === expectedTarget || resolvedTarget.startsWith(`${expectedTarget}/`);
|
|
7604
8657
|
} catch {
|
|
7605
8658
|
return false;
|
|
@@ -7608,7 +8661,7 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
|
|
|
7608
8661
|
async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
|
|
7609
8662
|
try {
|
|
7610
8663
|
if (!await pathExists6(settingsJsonPath)) return false;
|
|
7611
|
-
const raw = await
|
|
8664
|
+
const raw = await readFile11(settingsJsonPath, "utf8");
|
|
7612
8665
|
let parsed;
|
|
7613
8666
|
try {
|
|
7614
8667
|
parsed = JSON.parse(raw);
|
|
@@ -7635,11 +8688,11 @@ async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
|
|
|
7635
8688
|
}
|
|
7636
8689
|
}
|
|
7637
8690
|
async function detectLegacyInstall(opts = {}) {
|
|
7638
|
-
const home = opts.home ??
|
|
7639
|
-
const assetManifestPath =
|
|
7640
|
-
const legacyAssetsDir =
|
|
7641
|
-
const skillSymlinkPath =
|
|
7642
|
-
const settingsJsonPath =
|
|
8691
|
+
const home = opts.home ?? homedir13();
|
|
8692
|
+
const assetManifestPath = resolve17(home, LEGACY_ASSET_MANIFEST_RELATIVE);
|
|
8693
|
+
const legacyAssetsDir = resolve17(home, LEGACY_ASSETS_DIR_RELATIVE);
|
|
8694
|
+
const skillSymlinkPath = resolve17(home, LEGACY_SKILL_SYMLINK_RELATIVE);
|
|
8695
|
+
const settingsJsonPath = resolve17(home, CLAUDE_SETTINGS_JSON_RELATIVE);
|
|
7643
8696
|
const [hasAssetManifest, hasLegacySkillSymlink, legacyMcpWithoutEnabled] = await Promise.all([
|
|
7644
8697
|
pathExists6(assetManifestPath),
|
|
7645
8698
|
isSymlinkPointingInto(skillSymlinkPath, legacyAssetsDir),
|
|
@@ -7653,12 +8706,12 @@ async function detectLegacyInstall(opts = {}) {
|
|
|
7653
8706
|
};
|
|
7654
8707
|
}
|
|
7655
8708
|
async function detectLegacyVersion(opts = {}) {
|
|
7656
|
-
const home = opts.home ??
|
|
7657
|
-
const manifestPath =
|
|
8709
|
+
const home = opts.home ?? homedir13();
|
|
8710
|
+
const manifestPath = resolve17(home, LEGACY_ASSET_MANIFEST_RELATIVE);
|
|
7658
8711
|
try {
|
|
7659
|
-
const lst = await
|
|
8712
|
+
const lst = await stat7(manifestPath);
|
|
7660
8713
|
if (!lst.isFile()) return "0.7.x";
|
|
7661
|
-
const raw = await
|
|
8714
|
+
const raw = await readFile11(manifestPath, "utf8");
|
|
7662
8715
|
const parsed = JSON.parse(raw);
|
|
7663
8716
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7664
8717
|
const v = parsed.version;
|
|
@@ -7672,9 +8725,9 @@ async function detectLegacyVersion(opts = {}) {
|
|
|
7672
8725
|
|
|
7673
8726
|
// src/migration/cleanup.ts
|
|
7674
8727
|
async function cleanupLegacyDir(home) {
|
|
7675
|
-
const dir =
|
|
8728
|
+
const dir = resolve18(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
|
|
7676
8729
|
try {
|
|
7677
|
-
await
|
|
8730
|
+
await rm10(dir, { recursive: true, force: true });
|
|
7678
8731
|
return { ok: true };
|
|
7679
8732
|
} catch (err) {
|
|
7680
8733
|
return {
|
|
@@ -7684,7 +8737,7 @@ async function cleanupLegacyDir(home) {
|
|
|
7684
8737
|
}
|
|
7685
8738
|
}
|
|
7686
8739
|
async function cleanupLegacySymlink(home) {
|
|
7687
|
-
const path4 =
|
|
8740
|
+
const path4 = resolve18(home, LEGACY_SKILL_SYMLINK_RELATIVE);
|
|
7688
8741
|
try {
|
|
7689
8742
|
let lst;
|
|
7690
8743
|
try {
|
|
@@ -7706,7 +8759,7 @@ async function cleanupLegacySymlink(home) {
|
|
|
7706
8759
|
}
|
|
7707
8760
|
}
|
|
7708
8761
|
async function cleanupLegacyMcp(home) {
|
|
7709
|
-
const path4 =
|
|
8762
|
+
const path4 = resolve18(home, CLAUDE_SETTINGS_JSON_RELATIVE);
|
|
7710
8763
|
try {
|
|
7711
8764
|
const { data, existed } = await readJsonFile(
|
|
7712
8765
|
path4,
|
|
@@ -7734,7 +8787,7 @@ async function cleanupLegacyMcp(home) {
|
|
|
7734
8787
|
}
|
|
7735
8788
|
}
|
|
7736
8789
|
async function cleanupLegacyInstall(opts = {}) {
|
|
7737
|
-
const home = opts.home ??
|
|
8790
|
+
const home = opts.home ?? homedir14();
|
|
7738
8791
|
const legacyDir = await cleanupLegacyDir(home);
|
|
7739
8792
|
const skillSymlink = await cleanupLegacySymlink(home);
|
|
7740
8793
|
const settingsMcp = await cleanupLegacyMcp(home);
|
|
@@ -7762,13 +8815,13 @@ function errorMessage(err) {
|
|
|
7762
8815
|
}
|
|
7763
8816
|
|
|
7764
8817
|
// src/migration/log.ts
|
|
7765
|
-
import { appendFile as appendFile2, mkdir as
|
|
7766
|
-
import { homedir as
|
|
7767
|
-
import { dirname as
|
|
8818
|
+
import { appendFile as appendFile2, mkdir as mkdir9 } from "fs/promises";
|
|
8819
|
+
import { homedir as homedir15 } from "os";
|
|
8820
|
+
import { dirname as dirname9, resolve as resolve19 } from "path";
|
|
7768
8821
|
var MIGRATION_LOG_RELATIVE_PATH = ".rush/plugins/migration-failed.log";
|
|
7769
8822
|
function resolveMigrationLogPath(opts = {}) {
|
|
7770
|
-
const home = opts.home ??
|
|
7771
|
-
return
|
|
8823
|
+
const home = opts.home ?? homedir15();
|
|
8824
|
+
return resolve19(home, MIGRATION_LOG_RELATIVE_PATH);
|
|
7772
8825
|
}
|
|
7773
8826
|
async function appendMigrationFailure(reason, opts = {}) {
|
|
7774
8827
|
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -7785,20 +8838,20 @@ async function appendMigrationFailure(reason, opts = {}) {
|
|
|
7785
8838
|
const payload = `${lines.join("\n")}
|
|
7786
8839
|
`;
|
|
7787
8840
|
try {
|
|
7788
|
-
await
|
|
8841
|
+
await mkdir9(dirname9(logPath), { recursive: true });
|
|
7789
8842
|
await appendFile2(logPath, payload, { encoding: "utf8" });
|
|
7790
8843
|
} catch {
|
|
7791
8844
|
}
|
|
7792
8845
|
}
|
|
7793
8846
|
|
|
7794
8847
|
// src/migration/migrate.ts
|
|
7795
|
-
import { access as access11, readFile as
|
|
7796
|
-
import { homedir as
|
|
8848
|
+
import { access as access11, readFile as readFile13 } from "fs/promises";
|
|
8849
|
+
import { homedir as homedir17 } from "os";
|
|
7797
8850
|
|
|
7798
8851
|
// src/plugins/resolver.ts
|
|
7799
|
-
import { randomUUID as
|
|
7800
|
-
import { mkdir as
|
|
7801
|
-
import { homedir as
|
|
8852
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
8853
|
+
import { mkdir as mkdir10, rename as rename7, rm as rm11 } from "fs/promises";
|
|
8854
|
+
import { homedir as homedir16 } from "os";
|
|
7802
8855
|
import {
|
|
7803
8856
|
isAbsolute as isAbsolute4,
|
|
7804
8857
|
relative as pathRelative2,
|
|
@@ -7808,8 +8861,8 @@ import {
|
|
|
7808
8861
|
|
|
7809
8862
|
// src/plugins/capabilities.ts
|
|
7810
8863
|
import { constants as fsConstants8 } from "fs";
|
|
7811
|
-
import { access as access9, readdir as
|
|
7812
|
-
import { extname as extname2, resolve as
|
|
8864
|
+
import { access as access9, readdir as readdir6, stat as stat8 } from "fs/promises";
|
|
8865
|
+
import { extname as extname2, resolve as resolve20 } from "path";
|
|
7813
8866
|
var CAPABILITY_ORDER = [
|
|
7814
8867
|
"commands",
|
|
7815
8868
|
"skills",
|
|
@@ -7838,20 +8891,20 @@ async function scanCapabilities(sourceDir, manifest) {
|
|
|
7838
8891
|
return CAPABILITY_ORDER.filter((cap) => found.has(cap));
|
|
7839
8892
|
}
|
|
7840
8893
|
async function hasCommands(sourceDir) {
|
|
7841
|
-
return dirHasFileWithExt(
|
|
8894
|
+
return dirHasFileWithExt(resolve20(sourceDir, "commands"), ".md");
|
|
7842
8895
|
}
|
|
7843
8896
|
async function hasSkills(sourceDir) {
|
|
7844
|
-
const skillsDir =
|
|
8897
|
+
const skillsDir = resolve20(sourceDir, "skills");
|
|
7845
8898
|
if (await dirExists3(skillsDir)) {
|
|
7846
8899
|
let entries;
|
|
7847
8900
|
try {
|
|
7848
|
-
entries = await
|
|
8901
|
+
entries = await readdir6(skillsDir, { withFileTypes: true });
|
|
7849
8902
|
} catch {
|
|
7850
8903
|
return false;
|
|
7851
8904
|
}
|
|
7852
8905
|
for (const entry of entries) {
|
|
7853
8906
|
if (!entry.isDirectory()) continue;
|
|
7854
|
-
const skillMdPath =
|
|
8907
|
+
const skillMdPath = resolve20(skillsDir, entry.name, "SKILL.md");
|
|
7855
8908
|
if (await fileExists2(skillMdPath)) {
|
|
7856
8909
|
return true;
|
|
7857
8910
|
}
|
|
@@ -7860,16 +8913,16 @@ async function hasSkills(sourceDir) {
|
|
|
7860
8913
|
return hasClaudeStyleSkills(sourceDir);
|
|
7861
8914
|
}
|
|
7862
8915
|
async function hasRules(sourceDir) {
|
|
7863
|
-
return dirHasFileWithExt(
|
|
8916
|
+
return dirHasFileWithExt(resolve20(sourceDir, "rules"), ".md");
|
|
7864
8917
|
}
|
|
7865
8918
|
async function hasHooks(sourceDir) {
|
|
7866
|
-
const hooksDir =
|
|
8919
|
+
const hooksDir = resolve20(sourceDir, "hooks");
|
|
7867
8920
|
if (!await dirExists3(hooksDir)) {
|
|
7868
8921
|
return false;
|
|
7869
8922
|
}
|
|
7870
8923
|
let entries;
|
|
7871
8924
|
try {
|
|
7872
|
-
entries = await
|
|
8925
|
+
entries = await readdir6(hooksDir, { withFileTypes: true });
|
|
7873
8926
|
} catch {
|
|
7874
8927
|
return false;
|
|
7875
8928
|
}
|
|
@@ -7895,7 +8948,7 @@ async function dirHasFileWithExt(dirPath, ext) {
|
|
|
7895
8948
|
}
|
|
7896
8949
|
let entries;
|
|
7897
8950
|
try {
|
|
7898
|
-
entries = await
|
|
8951
|
+
entries = await readdir6(dirPath, { withFileTypes: true });
|
|
7899
8952
|
} catch {
|
|
7900
8953
|
return false;
|
|
7901
8954
|
}
|
|
@@ -7905,19 +8958,19 @@ async function dirHasFileWithExt(dirPath, ext) {
|
|
|
7905
8958
|
);
|
|
7906
8959
|
}
|
|
7907
8960
|
async function hasClaudeStyleSkills(sourceDir) {
|
|
7908
|
-
const dotSkillsDir =
|
|
8961
|
+
const dotSkillsDir = resolve20(sourceDir, ".skills");
|
|
7909
8962
|
if (!await dirExists3(dotSkillsDir)) {
|
|
7910
8963
|
return false;
|
|
7911
8964
|
}
|
|
7912
8965
|
async function walk2(dirPath) {
|
|
7913
8966
|
let entries;
|
|
7914
8967
|
try {
|
|
7915
|
-
entries = await
|
|
8968
|
+
entries = await readdir6(dirPath, { withFileTypes: true });
|
|
7916
8969
|
} catch {
|
|
7917
8970
|
return false;
|
|
7918
8971
|
}
|
|
7919
8972
|
for (const entry of entries) {
|
|
7920
|
-
const abs =
|
|
8973
|
+
const abs = resolve20(dirPath, entry.name);
|
|
7921
8974
|
if (entry.isDirectory()) {
|
|
7922
8975
|
if (await walk2(abs)) return true;
|
|
7923
8976
|
continue;
|
|
@@ -7932,7 +8985,7 @@ async function hasClaudeStyleSkills(sourceDir) {
|
|
|
7932
8985
|
}
|
|
7933
8986
|
async function dirExists3(p) {
|
|
7934
8987
|
try {
|
|
7935
|
-
const s = await
|
|
8988
|
+
const s = await stat8(p);
|
|
7936
8989
|
return s.isDirectory();
|
|
7937
8990
|
} catch {
|
|
7938
8991
|
return false;
|
|
@@ -7941,7 +8994,7 @@ async function dirExists3(p) {
|
|
|
7941
8994
|
async function fileExists2(p) {
|
|
7942
8995
|
try {
|
|
7943
8996
|
await access9(p, fsConstants8.R_OK);
|
|
7944
|
-
const s = await
|
|
8997
|
+
const s = await stat8(p);
|
|
7945
8998
|
return s.isFile();
|
|
7946
8999
|
} catch {
|
|
7947
9000
|
return false;
|
|
@@ -8011,17 +9064,17 @@ var PluginCloneFailedError = class extends PluginResolverError {
|
|
|
8011
9064
|
|
|
8012
9065
|
// src/plugins/manifest.ts
|
|
8013
9066
|
import { constants as fsConstants9 } from "fs";
|
|
8014
|
-
import { access as access10, readFile as
|
|
8015
|
-
import { resolve as
|
|
9067
|
+
import { access as access10, readFile as readFile12 } from "fs/promises";
|
|
9068
|
+
import { resolve as resolve21 } from "path";
|
|
8016
9069
|
var PLUGIN_MANIFEST_RELATIVE_PATH = ".claude-plugin/plugin.json";
|
|
8017
9070
|
async function readPluginManifest(pluginName, sourceDir) {
|
|
8018
|
-
const manifestPath =
|
|
9071
|
+
const manifestPath = resolve21(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
|
|
8019
9072
|
try {
|
|
8020
9073
|
await access10(manifestPath, fsConstants9.R_OK);
|
|
8021
9074
|
} catch {
|
|
8022
9075
|
throw new PluginManifestNotFoundError(pluginName, manifestPath);
|
|
8023
9076
|
}
|
|
8024
|
-
const raw = await
|
|
9077
|
+
const raw = await readFile12(manifestPath, "utf8");
|
|
8025
9078
|
return parsePluginManifest(pluginName, raw, manifestPath);
|
|
8026
9079
|
}
|
|
8027
9080
|
function parsePluginManifest(pluginName, raw, sourcePathForError) {
|
|
@@ -8236,7 +9289,7 @@ function pluginCacheDir(marketplaceName, pluginName) {
|
|
|
8236
9289
|
assertSafePathComponent(marketplaceName, "marketplace name");
|
|
8237
9290
|
assertSafePathComponent(pluginName, "plugin name");
|
|
8238
9291
|
return pathResolve(
|
|
8239
|
-
|
|
9292
|
+
homedir16(),
|
|
8240
9293
|
".rush",
|
|
8241
9294
|
"plugin-cache",
|
|
8242
9295
|
marketplaceName,
|
|
@@ -8271,8 +9324,8 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
|
|
|
8271
9324
|
}
|
|
8272
9325
|
const git = opts?.runner ?? defaultGitRunner;
|
|
8273
9326
|
const parent = pathResolve(cacheDir, "..");
|
|
8274
|
-
await
|
|
8275
|
-
const tmpDir = `${cacheDir}.${
|
|
9327
|
+
await mkdir10(parent, { recursive: true });
|
|
9328
|
+
const tmpDir = `${cacheDir}.${randomUUID7()}.tmp`;
|
|
8276
9329
|
try {
|
|
8277
9330
|
const args = ["clone", "--depth", "1"];
|
|
8278
9331
|
if (typeof src.ref === "string" && src.ref.length > 0) {
|
|
@@ -8298,16 +9351,16 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
|
|
|
8298
9351
|
}
|
|
8299
9352
|
}
|
|
8300
9353
|
try {
|
|
8301
|
-
await
|
|
9354
|
+
await rename7(tmpDir, cacheDir);
|
|
8302
9355
|
} catch (err) {
|
|
8303
9356
|
if (err.code === "ENOTEMPTY" && await pathExists3(cacheDir)) {
|
|
8304
|
-
await
|
|
9357
|
+
await rm11(tmpDir, { recursive: true, force: true });
|
|
8305
9358
|
return;
|
|
8306
9359
|
}
|
|
8307
9360
|
throw err;
|
|
8308
9361
|
}
|
|
8309
9362
|
} catch (err) {
|
|
8310
|
-
await
|
|
9363
|
+
await rm11(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
8311
9364
|
});
|
|
8312
9365
|
if (err instanceof PluginSourceUnresolvableError) throw err;
|
|
8313
9366
|
throw new PluginCloneFailedError(ref.name, marketplace.name, err);
|
|
@@ -8318,7 +9371,7 @@ async function ensurePluginCloned(ref, marketplace, entry, opts) {
|
|
|
8318
9371
|
var LEGACY_MIGRATION_KEY = "legacy-rush-0.7.x";
|
|
8319
9372
|
var SKIP_MIGRATION_ENV = "RUSH_SKIP_MIGRATION";
|
|
8320
9373
|
async function maybeRunMigration(input) {
|
|
8321
|
-
const home = input.home ??
|
|
9374
|
+
const home = input.home ?? homedir17();
|
|
8322
9375
|
const now = input.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
8323
9376
|
const envGet = input.env ?? ((key) => process.env[key]);
|
|
8324
9377
|
const reporter = input.reporter ?? {};
|
|
@@ -8546,7 +9599,7 @@ async function verifyNewFormat(manifestPath, expectedName, _expectedVersion) {
|
|
|
8546
9599
|
}
|
|
8547
9600
|
let raw;
|
|
8548
9601
|
try {
|
|
8549
|
-
raw = await
|
|
9602
|
+
raw = await readFile13(manifestPath, "utf8");
|
|
8550
9603
|
} catch (err) {
|
|
8551
9604
|
return {
|
|
8552
9605
|
ok: false,
|
|
@@ -8622,18 +9675,22 @@ var DEFAULT_TARGETS2 = [
|
|
|
8622
9675
|
"codex",
|
|
8623
9676
|
"cursor"
|
|
8624
9677
|
];
|
|
8625
|
-
var VALID_TARGETS =
|
|
8626
|
-
|
|
9678
|
+
var VALID_TARGETS = [
|
|
9679
|
+
...DEFAULT_TARGETS2,
|
|
9680
|
+
"claude-3p"
|
|
9681
|
+
];
|
|
9682
|
+
var VALID_TARGET_SET = new Set(VALID_TARGETS);
|
|
9683
|
+
function parseTargets2(raw, defaultTargets = DEFAULT_TARGETS2) {
|
|
8627
9684
|
if (raw === void 0 || raw.trim().length === 0) {
|
|
8628
|
-
return [...
|
|
9685
|
+
return [...defaultTargets];
|
|
8629
9686
|
}
|
|
8630
9687
|
const segments = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
8631
9688
|
const seen = /* @__PURE__ */ new Set();
|
|
8632
9689
|
const out = [];
|
|
8633
9690
|
for (const seg of segments) {
|
|
8634
|
-
if (!
|
|
9691
|
+
if (!VALID_TARGET_SET.has(seg)) {
|
|
8635
9692
|
throw new RushError(
|
|
8636
|
-
`Invalid --target '${seg}'. Expected one of: ${[...
|
|
9693
|
+
`Invalid --target '${seg}'. Expected one of: ${[...VALID_TARGETS].join(", ")}`,
|
|
8637
9694
|
{ raw: seg },
|
|
8638
9695
|
"INVALID_TARGET",
|
|
8639
9696
|
2
|
|
@@ -8646,10 +9703,60 @@ function parseTargets2(raw) {
|
|
|
8646
9703
|
}
|
|
8647
9704
|
}
|
|
8648
9705
|
if (out.length === 0) {
|
|
8649
|
-
return [...
|
|
9706
|
+
return [...defaultTargets];
|
|
9707
|
+
}
|
|
9708
|
+
return out;
|
|
9709
|
+
}
|
|
9710
|
+
function maintenanceTargetsFromRegistry(registry) {
|
|
9711
|
+
const installedTargets = [];
|
|
9712
|
+
for (const [, entry] of registry.list()) {
|
|
9713
|
+
for (const target of Object.keys(entry.targets)) {
|
|
9714
|
+
if (isInstallTarget(target)) installedTargets.push(target);
|
|
9715
|
+
}
|
|
9716
|
+
}
|
|
9717
|
+
return mergeTargets(DEFAULT_TARGETS2, installedTargets);
|
|
9718
|
+
}
|
|
9719
|
+
function maintenanceTargetsForRef(registry, ref) {
|
|
9720
|
+
const entry = registry.get(ref);
|
|
9721
|
+
if (!entry) return [...DEFAULT_TARGETS2];
|
|
9722
|
+
const installedTargets = Object.keys(entry.targets).filter(isInstallTarget);
|
|
9723
|
+
return installedTargets.length > 0 ? installedTargets : [...DEFAULT_TARGETS2];
|
|
9724
|
+
}
|
|
9725
|
+
function claude3pAccountFromRegistryEntry(entry) {
|
|
9726
|
+
const claude3p = entry?.targets["claude-3p"];
|
|
9727
|
+
if (!claude3p) return void 0;
|
|
9728
|
+
for (const file2 of claude3p.files) {
|
|
9729
|
+
const parsed = parseClaude3pAccountFromPath(file2);
|
|
9730
|
+
if (parsed) return parsed;
|
|
9731
|
+
}
|
|
9732
|
+
return void 0;
|
|
9733
|
+
}
|
|
9734
|
+
function parseClaude3pAccountFromPath(filePath) {
|
|
9735
|
+
const parts = filePath.split(/[\\/]+/).filter(Boolean);
|
|
9736
|
+
const sessionsIdx = parts.lastIndexOf(LOCAL_AGENT_SESSIONS_DIR);
|
|
9737
|
+
if (sessionsIdx < 0) return void 0;
|
|
9738
|
+
const accountUuid = parts[sessionsIdx + 1];
|
|
9739
|
+
const orgUuid = parts[sessionsIdx + 2];
|
|
9740
|
+
const coworkSegment = parts[sessionsIdx + 3];
|
|
9741
|
+
if (accountUuid === void 0 || orgUuid === void 0 || coworkSegment !== COWORK_PLUGINS_DIR || !UUID_V4_RE.test(accountUuid) || !UUID_V4_RE.test(orgUuid)) {
|
|
9742
|
+
return void 0;
|
|
9743
|
+
}
|
|
9744
|
+
return { accountUuid, orgUuid };
|
|
9745
|
+
}
|
|
9746
|
+
function mergeTargets(fallback, extra) {
|
|
9747
|
+
const out = [];
|
|
9748
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9749
|
+
for (const target of [...fallback, ...extra]) {
|
|
9750
|
+
if (!seen.has(target)) {
|
|
9751
|
+
seen.add(target);
|
|
9752
|
+
out.push(target);
|
|
9753
|
+
}
|
|
8650
9754
|
}
|
|
8651
9755
|
return out;
|
|
8652
9756
|
}
|
|
9757
|
+
function isInstallTarget(value) {
|
|
9758
|
+
return VALID_TARGET_SET.has(value);
|
|
9759
|
+
}
|
|
8653
9760
|
async function parsePluginRef(raw, cache) {
|
|
8654
9761
|
const trimmed = raw.trim();
|
|
8655
9762
|
if (trimmed.length === 0) {
|
|
@@ -8756,6 +9863,20 @@ function defaultInstallerFactory(target, opts = {}) {
|
|
|
8756
9863
|
};
|
|
8757
9864
|
return new CodexInstaller(cxOpts);
|
|
8758
9865
|
}
|
|
9866
|
+
case "claude-3p": {
|
|
9867
|
+
const cpOpts = {};
|
|
9868
|
+
if (opts.home !== void 0) cpOpts.home = opts.home;
|
|
9869
|
+
if (opts.claude3pAccount?.accountUuid !== void 0) {
|
|
9870
|
+
cpOpts.accountUuid = opts.claude3pAccount.accountUuid;
|
|
9871
|
+
}
|
|
9872
|
+
if (opts.claude3pAccount?.orgUuid !== void 0) {
|
|
9873
|
+
cpOpts.orgUuid = opts.claude3pAccount.orgUuid;
|
|
9874
|
+
}
|
|
9875
|
+
if (opts.claude3pMarketplaceSource !== void 0) {
|
|
9876
|
+
cpOpts.marketplaceSource = opts.claude3pMarketplaceSource;
|
|
9877
|
+
}
|
|
9878
|
+
return new ClaudeCpInstaller(cpOpts);
|
|
9879
|
+
}
|
|
8759
9880
|
default: {
|
|
8760
9881
|
const _exhaustive = target;
|
|
8761
9882
|
void _exhaustive;
|
|
@@ -8813,6 +9934,9 @@ function claudeMarketplaceSourceFrom(marketplace) {
|
|
|
8813
9934
|
return void 0;
|
|
8814
9935
|
}
|
|
8815
9936
|
}
|
|
9937
|
+
function claude3pMarketplaceSourceFrom(marketplace) {
|
|
9938
|
+
return { rootDir: marketplace.rootDir };
|
|
9939
|
+
}
|
|
8816
9940
|
function buildRegistryEntry(plugin, results, nowIso) {
|
|
8817
9941
|
const nonSkipped = results.filter((r) => r.status !== "skipped");
|
|
8818
9942
|
if (nonSkipped.length === 0) {
|
|
@@ -8879,7 +10003,11 @@ async function runInstall(input) {
|
|
|
8879
10003
|
const dryRun = input.dryRun === true;
|
|
8880
10004
|
const now = input.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
8881
10005
|
const cache = input.cache ?? new MarketplaceCache(input.home !== void 0 ? { home: input.home } : {});
|
|
10006
|
+
output.dim(` Resolving plugin reference: ${input.ref}`);
|
|
8882
10007
|
const ref = await parsePluginRef(input.ref, cache);
|
|
10008
|
+
output.dim(
|
|
10009
|
+
` Loading marketplace '${ref.marketplace}' for plugin '${ref.name}'`
|
|
10010
|
+
);
|
|
8883
10011
|
let marketplace;
|
|
8884
10012
|
try {
|
|
8885
10013
|
marketplace = await cache.get(ref.marketplace);
|
|
@@ -8887,13 +10015,19 @@ async function runInstall(input) {
|
|
|
8887
10015
|
if (err instanceof MarketplaceNotFoundError) {
|
|
8888
10016
|
if (ref.marketplace === "rush" || ref.marketplace === "rush-marketplace") {
|
|
8889
10017
|
const apiHost = getGlobalConfig().api.replace(/^https?:\/\//, "");
|
|
10018
|
+
output.dim(` Registering Rush marketplace from ${apiHost}`);
|
|
8890
10019
|
const rushSource = parseSource(`rush://${apiHost}`);
|
|
8891
10020
|
await cache.add(rushSource, { as: ref.marketplace });
|
|
8892
10021
|
marketplace = await cache.get(ref.marketplace);
|
|
8893
10022
|
} else if (ref.marketplace === "claude-plugins-official") {
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
10023
|
+
throw new RushError(
|
|
10024
|
+
`Marketplace '@claude-plugins-official' is no longer supported. All plugins (Rush + Claude Code \u5B98\u65B9) are now under '@rush'.
|
|
10025
|
+
Try:
|
|
10026
|
+
rush-ai plugin install ${ref.name}@rush`,
|
|
10027
|
+
{ marketplaceName: ref.marketplace, pluginName: ref.name },
|
|
10028
|
+
"MARKETPLACE_DEPRECATED",
|
|
10029
|
+
2
|
|
10030
|
+
);
|
|
8897
10031
|
} else {
|
|
8898
10032
|
throw new RushError(
|
|
8899
10033
|
`Marketplace '${ref.marketplace}' is not in the local cache. Run 'rush-ai marketplace add <source>' first.`,
|
|
@@ -8915,6 +10049,7 @@ async function runInstall(input) {
|
|
|
8915
10049
|
const cacheEntry = cacheList.find((m) => m.name === ref.marketplace);
|
|
8916
10050
|
const isStale = cacheEntry?.cachedAt ? Date.now() - new Date(cacheEntry.cachedAt).getTime() > TWENTY_FOUR_HOURS : false;
|
|
8917
10051
|
if (entryMissing || isStale) {
|
|
10052
|
+
output.dim(` Refreshing marketplace '${ref.marketplace}'`);
|
|
8918
10053
|
marketplace = await cache.update(ref.marketplace);
|
|
8919
10054
|
}
|
|
8920
10055
|
if (!marketplace.manifest.plugins.some((p) => p.name === ref.name)) {
|
|
@@ -8927,8 +10062,8 @@ async function runInstall(input) {
|
|
|
8927
10062
|
}
|
|
8928
10063
|
}
|
|
8929
10064
|
if (marketplace.source.kind === "rush") {
|
|
8930
|
-
const pluginDir =
|
|
8931
|
-
const manifestPath =
|
|
10065
|
+
const pluginDir = resolve22(marketplace.rootDir, "plugins", ref.name);
|
|
10066
|
+
const manifestPath = resolve22(pluginDir, ".claude-plugin/plugin.json");
|
|
8932
10067
|
const alreadyMaterialized = await pathExists3(manifestPath);
|
|
8933
10068
|
const hasNewSecrets = input.secrets && Object.keys(input.secrets).length > 0;
|
|
8934
10069
|
const shouldMaterialize = !alreadyMaterialized || force || hasNewSecrets;
|
|
@@ -8943,6 +10078,7 @@ async function runInstall(input) {
|
|
|
8943
10078
|
if (shouldMaterialize && !dryRun) {
|
|
8944
10079
|
const rushSource = marketplace.source;
|
|
8945
10080
|
const token = getAuthToken();
|
|
10081
|
+
output.dim(` Fetching plugin manifest: ${ref.name}@${ref.marketplace}`);
|
|
8946
10082
|
let apiManifest;
|
|
8947
10083
|
try {
|
|
8948
10084
|
apiManifest = await fetchRushPlugin(rushSource, ref.name, { token });
|
|
@@ -8974,6 +10110,9 @@ async function runInstall(input) {
|
|
|
8974
10110
|
secrets = { ...prompted, ...presetSecrets };
|
|
8975
10111
|
}
|
|
8976
10112
|
try {
|
|
10113
|
+
output.dim(
|
|
10114
|
+
` Downloading plugin assets: ${ref.name}@${ref.marketplace}`
|
|
10115
|
+
);
|
|
8977
10116
|
await materializeRushPlugin(rushSource, ref.name, pluginDir, {
|
|
8978
10117
|
token,
|
|
8979
10118
|
secrets
|
|
@@ -8994,7 +10133,7 @@ async function runInstall(input) {
|
|
|
8994
10133
|
if (force && !dryRun) {
|
|
8995
10134
|
const entry = marketplace.manifest.plugins.find((p) => p.name === ref.name);
|
|
8996
10135
|
if (entry && hasUrlSource(entry)) {
|
|
8997
|
-
await
|
|
10136
|
+
await rm12(pluginCacheDir(ref.marketplace, ref.name), {
|
|
8998
10137
|
recursive: true,
|
|
8999
10138
|
force: true
|
|
9000
10139
|
});
|
|
@@ -9003,6 +10142,9 @@ async function runInstall(input) {
|
|
|
9003
10142
|
const resolveFn = input.resolvePluginFn ?? resolvePlugin;
|
|
9004
10143
|
let plugin;
|
|
9005
10144
|
try {
|
|
10145
|
+
output.dim(
|
|
10146
|
+
` Reading local plugin package: ${ref.name}@${ref.marketplace}`
|
|
10147
|
+
);
|
|
9006
10148
|
plugin = await resolveFn(ref, marketplace, { dryRun });
|
|
9007
10149
|
} catch (err) {
|
|
9008
10150
|
if (err instanceof PluginNotFoundInMarketplaceError && marketplace.source.kind === "rush") {
|
|
@@ -9077,6 +10219,12 @@ async function runInstall(input) {
|
|
|
9077
10219
|
claudeMarketplaceSource: claudeMarketplaceSourceFrom(
|
|
9078
10220
|
opts.marketplace
|
|
9079
10221
|
)
|
|
10222
|
+
} : {},
|
|
10223
|
+
...target === "claude-3p" ? {
|
|
10224
|
+
claude3pMarketplaceSource: claude3pMarketplaceSourceFrom(
|
|
10225
|
+
opts.marketplace
|
|
10226
|
+
),
|
|
10227
|
+
...input.claude3pAccount !== void 0 ? { claude3pAccount: input.claude3pAccount } : {}
|
|
9080
10228
|
} : {}
|
|
9081
10229
|
}));
|
|
9082
10230
|
const installer = factory(t, {
|
|
@@ -9125,10 +10273,10 @@ async function runInstall(input) {
|
|
|
9125
10273
|
if (!dryRun && codexResult?.status === "ok" && !skipCodexMirrorSync(input)) {
|
|
9126
10274
|
try {
|
|
9127
10275
|
const [{ syncCodexMarketplaceMirror: syncCodexMarketplaceMirror2 }, { pickContentLoader: pickContentLoader2 }] = await Promise.all([
|
|
9128
|
-
import("./mirror-
|
|
9129
|
-
import("./_codex-content-loader-
|
|
10276
|
+
import("./mirror-SQ6GNVP5.js"),
|
|
10277
|
+
import("./_codex-content-loader-WAPA56U3.js")
|
|
9130
10278
|
]);
|
|
9131
|
-
await syncCodexMarketplaceMirror2(input.home ??
|
|
10279
|
+
await syncCodexMarketplaceMirror2(input.home ?? homedir18(), marketplace, {
|
|
9132
10280
|
contentLoader: pickContentLoader2(marketplace)
|
|
9133
10281
|
});
|
|
9134
10282
|
} catch (err) {
|
|
@@ -9174,7 +10322,7 @@ function formatRegistryError(err) {
|
|
|
9174
10322
|
}
|
|
9175
10323
|
function registerInstallCommand(group, _root) {
|
|
9176
10324
|
group.command("install").description(
|
|
9177
|
-
"Install a plugin to one or more IDEs (Claude Code, Codex, Cursor)"
|
|
10325
|
+
"Install a plugin to one or more IDEs (Claude Code, Codex, Cursor, Claude-3p)"
|
|
9178
10326
|
).argument("<ref>", "plugin reference: <name> or <name>@<marketplace>").option(
|
|
9179
10327
|
"--target <list>",
|
|
9180
10328
|
`comma-separated targets (default: ${[...DEFAULT_TARGETS2].join(",")})`
|
|
@@ -9288,6 +10436,8 @@ function targetDisplayName(t) {
|
|
|
9288
10436
|
return "Codex";
|
|
9289
10437
|
case "cursor":
|
|
9290
10438
|
return "Cursor";
|
|
10439
|
+
case "claude-3p":
|
|
10440
|
+
return "Claude-3p";
|
|
9291
10441
|
default: {
|
|
9292
10442
|
const _exhaustive = t;
|
|
9293
10443
|
void _exhaustive;
|
|
@@ -9303,6 +10453,8 @@ function nextStepFor(t) {
|
|
|
9303
10453
|
return "fully quit Codex (Cmd+Q on macOS) and reopen \u2014 Codex does not hot-reload config.toml; just opening a new chat is not enough";
|
|
9304
10454
|
case "cursor":
|
|
9305
10455
|
return "restart Cursor";
|
|
10456
|
+
case "claude-3p":
|
|
10457
|
+
return "start a new Claude-3p conversation; if the plugin is still missing, fully quit Claude-3p (Cmd+Q on macOS) and reopen";
|
|
9306
10458
|
default:
|
|
9307
10459
|
return "restart the IDE";
|
|
9308
10460
|
}
|
|
@@ -9310,11 +10462,14 @@ function nextStepFor(t) {
|
|
|
9310
10462
|
|
|
9311
10463
|
// src/commands/plugin/list.ts
|
|
9312
10464
|
async function runList2(input = {}) {
|
|
9313
|
-
const targets = parseTargets2(input.targetRaw);
|
|
9314
10465
|
const cache = input.cache ?? new MarketplaceCache(input.home !== void 0 ? { home: input.home } : {});
|
|
9315
10466
|
const registry = input.registry ?? await RushRegistryStore.load(
|
|
9316
10467
|
input.home !== void 0 ? { home: input.home } : {}
|
|
9317
10468
|
);
|
|
10469
|
+
const targets = parseTargets2(
|
|
10470
|
+
input.targetRaw,
|
|
10471
|
+
maintenanceTargetsFromRegistry(registry)
|
|
10472
|
+
);
|
|
9318
10473
|
if (input.available) {
|
|
9319
10474
|
return runAvailable(cache, registry, targets);
|
|
9320
10475
|
}
|
|
@@ -9461,16 +10616,15 @@ function statusIcon2(status) {
|
|
|
9461
10616
|
|
|
9462
10617
|
// src/commands/plugin/uninstall.ts
|
|
9463
10618
|
async function runUninstall(input) {
|
|
9464
|
-
const targets = parseTargets2(input.targetRaw);
|
|
9465
10619
|
const targetExplicit = input.targetExplicit ?? input.targetRaw !== void 0;
|
|
9466
10620
|
const dryRun = input.dryRun === true;
|
|
10621
|
+
const registry = input.registry ?? await RushRegistryStore.load(
|
|
10622
|
+
input.home !== void 0 ? { home: input.home } : {}
|
|
10623
|
+
);
|
|
9467
10624
|
const cache = input.cache ?? new MarketplaceCache(input.home !== void 0 ? { home: input.home } : {});
|
|
9468
10625
|
const ref = await parsePluginRef(input.ref, cache).catch(async (err) => {
|
|
9469
10626
|
if (err instanceof RushError && err.code === "PLUGIN_NOT_FOUND") {
|
|
9470
|
-
const
|
|
9471
|
-
input.home !== void 0 ? { home: input.home } : {}
|
|
9472
|
-
);
|
|
9473
|
-
const all = reg.list();
|
|
10627
|
+
const all = registry.list();
|
|
9474
10628
|
const matches = all.filter(([r]) => r.name === input.ref.trim());
|
|
9475
10629
|
if (matches.length === 1) {
|
|
9476
10630
|
const only = matches[0];
|
|
@@ -9488,13 +10642,20 @@ async function runUninstall(input) {
|
|
|
9488
10642
|
}
|
|
9489
10643
|
throw err;
|
|
9490
10644
|
});
|
|
10645
|
+
const targets = parseTargets2(
|
|
10646
|
+
input.targetRaw,
|
|
10647
|
+
maintenanceTargetsForRef(registry, ref)
|
|
10648
|
+
);
|
|
10649
|
+
const claude3pAccount = claude3pAccountFromRegistryEntry(registry.get(ref));
|
|
9491
10650
|
const results = [];
|
|
9492
10651
|
for (const t of targets) {
|
|
9493
10652
|
const factory = input.installerFactory ?? ((target, opts) => defaultInstallerFactory(target, {
|
|
9494
|
-
...opts.home !== void 0 ? { home: opts.home } : {}
|
|
10653
|
+
...opts.home !== void 0 ? { home: opts.home } : {},
|
|
10654
|
+
...target === "claude-3p" && claude3pAccount !== void 0 ? { claude3pAccount } : {}
|
|
9495
10655
|
}));
|
|
9496
10656
|
const installer = factory(t, {
|
|
9497
|
-
...input.home !== void 0 ? { home: input.home } : {}
|
|
10657
|
+
...input.home !== void 0 ? { home: input.home } : {},
|
|
10658
|
+
...t === "claude-3p" && claude3pAccount !== void 0 ? { claude3pAccount } : {}
|
|
9498
10659
|
});
|
|
9499
10660
|
try {
|
|
9500
10661
|
const result = await installer.uninstall(ref, { dryRun });
|
|
@@ -9523,11 +10684,8 @@ async function runUninstall(input) {
|
|
|
9523
10684
|
let registryError;
|
|
9524
10685
|
if (!dryRun) {
|
|
9525
10686
|
try {
|
|
9526
|
-
const store = input.registry ?? await RushRegistryStore.load(
|
|
9527
|
-
input.home !== void 0 ? { home: input.home } : {}
|
|
9528
|
-
);
|
|
9529
10687
|
const successTargets = results.filter((r) => r.status === "ok").map((r) => r.target);
|
|
9530
|
-
await applyUninstallToRegistry(
|
|
10688
|
+
await applyUninstallToRegistry(registry, ref, successTargets);
|
|
9531
10689
|
} catch (err) {
|
|
9532
10690
|
registryError = formatRegistryError2(err);
|
|
9533
10691
|
}
|
|
@@ -9612,6 +10770,16 @@ function printUninstallSummary(result) {
|
|
|
9612
10770
|
}
|
|
9613
10771
|
}
|
|
9614
10772
|
}
|
|
10773
|
+
const claude3pOk = results.some(
|
|
10774
|
+
(r) => r.target === "claude-3p" && r.status === "ok"
|
|
10775
|
+
);
|
|
10776
|
+
if (claude3pOk && !dryRun) {
|
|
10777
|
+
output.newline();
|
|
10778
|
+
output.dim("Next steps:");
|
|
10779
|
+
output.dim(
|
|
10780
|
+
" - Claude-3p: start a new Claude-3p conversation; if the plugin is still visible, fully quit Claude-3p (Cmd+Q on macOS) and reopen"
|
|
10781
|
+
);
|
|
10782
|
+
}
|
|
9615
10783
|
if (result.registryError) {
|
|
9616
10784
|
output.newline();
|
|
9617
10785
|
output.warn(result.registryError);
|
|
@@ -9642,6 +10810,8 @@ function targetDisplayName2(t) {
|
|
|
9642
10810
|
return "Codex";
|
|
9643
10811
|
case "cursor":
|
|
9644
10812
|
return "Cursor";
|
|
10813
|
+
case "claude-3p":
|
|
10814
|
+
return "Claude-3p";
|
|
9645
10815
|
default: {
|
|
9646
10816
|
const _exhaustive = t;
|
|
9647
10817
|
void _exhaustive;
|
|
@@ -9652,13 +10822,16 @@ function targetDisplayName2(t) {
|
|
|
9652
10822
|
|
|
9653
10823
|
// src/commands/plugin/update.ts
|
|
9654
10824
|
async function runUpdate3(input = {}) {
|
|
9655
|
-
const targets = parseTargets2(input.targetRaw);
|
|
9656
10825
|
const targetExplicit = input.targetExplicit ?? input.targetRaw !== void 0;
|
|
9657
10826
|
const dryRun = input.dryRun === true;
|
|
9658
10827
|
const cache = input.cache ?? new MarketplaceCache(input.home !== void 0 ? { home: input.home } : {});
|
|
9659
10828
|
const registry = input.registry ?? await RushRegistryStore.load(
|
|
9660
10829
|
input.home !== void 0 ? { home: input.home } : {}
|
|
9661
10830
|
);
|
|
10831
|
+
const targets = parseTargets2(
|
|
10832
|
+
input.targetRaw,
|
|
10833
|
+
maintenanceTargetsFromRegistry(registry)
|
|
10834
|
+
);
|
|
9662
10835
|
const refs = await collectRefs(input, cache, registry);
|
|
9663
10836
|
if (refs.length === 0) {
|
|
9664
10837
|
return {
|
|
@@ -9724,14 +10897,16 @@ async function runUpdate3(input = {}) {
|
|
|
9724
10897
|
plugins.push({ ref, fromVersion, toVersion, upToDate: false });
|
|
9725
10898
|
continue;
|
|
9726
10899
|
}
|
|
10900
|
+
const claude3pAccount = input.targetRaw === void 0 ? claude3pAccountFromRegistryEntry(registry.get(ref)) : void 0;
|
|
9727
10901
|
const installInput = {
|
|
9728
10902
|
ref: `${ref.name}@${ref.marketplace}`,
|
|
9729
|
-
...input.targetRaw !== void 0 ? { targetRaw: input.targetRaw } : {},
|
|
10903
|
+
...input.targetRaw !== void 0 ? { targetRaw: input.targetRaw } : { targetRaw: maintenanceTargetsForRef(registry, ref).join(",") },
|
|
9730
10904
|
targetExplicit,
|
|
9731
10905
|
force: true,
|
|
9732
10906
|
...input.home !== void 0 ? { home: input.home } : {},
|
|
9733
10907
|
cache,
|
|
9734
|
-
registry
|
|
10908
|
+
registry,
|
|
10909
|
+
...claude3pAccount !== void 0 ? { claude3pAccount } : {}
|
|
9735
10910
|
};
|
|
9736
10911
|
const result = await installRunner(installInput);
|
|
9737
10912
|
plugins.push({
|
|
@@ -9939,7 +11114,7 @@ function getReskillInvocation() {
|
|
|
9939
11114
|
};
|
|
9940
11115
|
}
|
|
9941
11116
|
function runReskill(args) {
|
|
9942
|
-
return new Promise((
|
|
11117
|
+
return new Promise((resolve25, reject) => {
|
|
9943
11118
|
const reskill = getReskillInvocation();
|
|
9944
11119
|
const child = spawn2(reskill.command, [...reskill.args, ...args], {
|
|
9945
11120
|
env: createReskillEnv(),
|
|
@@ -9949,10 +11124,10 @@ function runReskill(args) {
|
|
|
9949
11124
|
child.on("close", (code, signal) => {
|
|
9950
11125
|
if (signal) {
|
|
9951
11126
|
output.error(`reskill exited from signal ${signal}`);
|
|
9952
|
-
|
|
11127
|
+
resolve25(1);
|
|
9953
11128
|
return;
|
|
9954
11129
|
}
|
|
9955
|
-
|
|
11130
|
+
resolve25(code ?? 1);
|
|
9956
11131
|
});
|
|
9957
11132
|
});
|
|
9958
11133
|
}
|
|
@@ -10026,7 +11201,7 @@ function registerSkillCommand(program) {
|
|
|
10026
11201
|
|
|
10027
11202
|
// src/commands/task/index.ts
|
|
10028
11203
|
import { createWriteStream } from "fs";
|
|
10029
|
-
import { mkdir as
|
|
11204
|
+
import { mkdir as mkdir11, readFile as readFile14, stat as stat9 } from "fs/promises";
|
|
10030
11205
|
import path3 from "path";
|
|
10031
11206
|
import { Readable } from "stream";
|
|
10032
11207
|
import { pipeline } from "stream/promises";
|
|
@@ -10052,13 +11227,13 @@ async function readStdinIfPiped() {
|
|
|
10052
11227
|
if (process.stdin.isTTY) {
|
|
10053
11228
|
return null;
|
|
10054
11229
|
}
|
|
10055
|
-
return new Promise((
|
|
11230
|
+
return new Promise((resolve25, reject) => {
|
|
10056
11231
|
const chunks = [];
|
|
10057
11232
|
process.stdin.on("data", (chunk) => {
|
|
10058
11233
|
chunks.push(chunk);
|
|
10059
11234
|
});
|
|
10060
11235
|
process.stdin.on("end", () => {
|
|
10061
|
-
|
|
11236
|
+
resolve25(Buffer.concat(chunks).toString("utf-8").trim());
|
|
10062
11237
|
});
|
|
10063
11238
|
process.stdin.on("error", reject);
|
|
10064
11239
|
});
|
|
@@ -10259,7 +11434,7 @@ function buildDeployUrl(prefix, env, apiBaseUrl) {
|
|
|
10259
11434
|
}
|
|
10260
11435
|
|
|
10261
11436
|
// src/commands/task/push.ts
|
|
10262
|
-
import { resolve as
|
|
11437
|
+
import { resolve as resolve23 } from "path";
|
|
10263
11438
|
import chalk5 from "chalk";
|
|
10264
11439
|
|
|
10265
11440
|
// src/util/env-file.ts
|
|
@@ -10403,7 +11578,7 @@ function registerPushSubcommand(task, program) {
|
|
|
10403
11578
|
requireAuth();
|
|
10404
11579
|
const format = resolveFormat(program.opts());
|
|
10405
11580
|
const client = createClient();
|
|
10406
|
-
const projectPath =
|
|
11581
|
+
const projectPath = resolve23(opts.path);
|
|
10407
11582
|
if (format !== "json") {
|
|
10408
11583
|
output.info("Checking project readiness...");
|
|
10409
11584
|
}
|
|
@@ -10834,7 +12009,7 @@ async function confirmPublish(args) {
|
|
|
10834
12009
|
return answer.trim().toLowerCase().startsWith("y");
|
|
10835
12010
|
}
|
|
10836
12011
|
function readOneLine() {
|
|
10837
|
-
return new Promise((
|
|
12012
|
+
return new Promise((resolve25) => {
|
|
10838
12013
|
let acc = "";
|
|
10839
12014
|
const onData = (chunk) => {
|
|
10840
12015
|
acc += chunk.toString("utf-8");
|
|
@@ -10842,7 +12017,7 @@ function readOneLine() {
|
|
|
10842
12017
|
if (nlIdx !== -1) {
|
|
10843
12018
|
process.stdin.removeListener("data", onData);
|
|
10844
12019
|
process.stdin.pause();
|
|
10845
|
-
|
|
12020
|
+
resolve25(acc.slice(0, nlIdx));
|
|
10846
12021
|
}
|
|
10847
12022
|
};
|
|
10848
12023
|
process.stdin.resume();
|
|
@@ -11028,12 +12203,12 @@ function registerDomainSubcommand(task, program) {
|
|
|
11028
12203
|
}
|
|
11029
12204
|
|
|
11030
12205
|
// src/commands/task/env.ts
|
|
11031
|
-
import { resolve as
|
|
12206
|
+
import { resolve as resolve24 } from "path";
|
|
11032
12207
|
|
|
11033
12208
|
// src/util/prompt.ts
|
|
11034
12209
|
import { createInterface } from "readline";
|
|
11035
12210
|
function promptSecret(message) {
|
|
11036
|
-
return new Promise((
|
|
12211
|
+
return new Promise((resolve25, reject) => {
|
|
11037
12212
|
const rl = createInterface({
|
|
11038
12213
|
input: process.stdin,
|
|
11039
12214
|
output: process.stdout,
|
|
@@ -11046,7 +12221,7 @@ function promptSecret(message) {
|
|
|
11046
12221
|
rl.question("", (answer) => {
|
|
11047
12222
|
rl.close();
|
|
11048
12223
|
process.stdout.write("\n");
|
|
11049
|
-
|
|
12224
|
+
resolve25(answer);
|
|
11050
12225
|
});
|
|
11051
12226
|
rl.on("SIGINT", () => {
|
|
11052
12227
|
rl.close();
|
|
@@ -11069,7 +12244,7 @@ var NO_PROJECT_HINT2 = [
|
|
|
11069
12244
|
].join("\n");
|
|
11070
12245
|
function resolveProjectId(opts) {
|
|
11071
12246
|
if (opts.project) return Promise.resolve(opts.project);
|
|
11072
|
-
const projectPath =
|
|
12247
|
+
const projectPath = resolve24(opts.path ?? ".");
|
|
11073
12248
|
const remoteUrl = isGitRepo(projectPath) ? getRemoteUrl(projectPath) : null;
|
|
11074
12249
|
const idFromRemote = remoteUrl ? extractRushProjectId(remoteUrl) : null;
|
|
11075
12250
|
if (idFromRemote) return Promise.resolve(idFromRemote);
|
|
@@ -11797,7 +12972,7 @@ async function downloadFile(file2, destPath, client) {
|
|
|
11797
12972
|
if (!response.body) {
|
|
11798
12973
|
throw new Error("No response body");
|
|
11799
12974
|
}
|
|
11800
|
-
await
|
|
12975
|
+
await mkdir11(path3.dirname(destPath), { recursive: true });
|
|
11801
12976
|
const nodeStream = Readable.fromWeb(
|
|
11802
12977
|
response.body
|
|
11803
12978
|
);
|
|
@@ -12338,7 +13513,7 @@ async function uploadFileForTask(client, localPath) {
|
|
|
12338
13513
|
const resolved = path3.resolve(localPath);
|
|
12339
13514
|
let fileStat;
|
|
12340
13515
|
try {
|
|
12341
|
-
fileStat = await
|
|
13516
|
+
fileStat = await stat9(resolved);
|
|
12342
13517
|
} catch {
|
|
12343
13518
|
throw new RushError(`File not found: ${localPath}`);
|
|
12344
13519
|
}
|
|
@@ -12352,7 +13527,7 @@ async function uploadFileForTask(client, localPath) {
|
|
|
12352
13527
|
}
|
|
12353
13528
|
const filename = path3.basename(resolved);
|
|
12354
13529
|
const mediaType = guessMediaType(resolved);
|
|
12355
|
-
const buf = await
|
|
13530
|
+
const buf = await readFile14(resolved);
|
|
12356
13531
|
const blob = new File([buf], filename, { type: mediaType });
|
|
12357
13532
|
const formData = new FormData();
|
|
12358
13533
|
formData.append("file", blob);
|
|
@@ -12405,9 +13580,9 @@ function registerCommands(program) {
|
|
|
12405
13580
|
}
|
|
12406
13581
|
|
|
12407
13582
|
// src/util/update-check.ts
|
|
12408
|
-
import { mkdir as
|
|
12409
|
-
import { homedir as
|
|
12410
|
-
import { dirname as
|
|
13583
|
+
import { mkdir as mkdir12, readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
|
|
13584
|
+
import { homedir as homedir19 } from "os";
|
|
13585
|
+
import { dirname as dirname10, join as join11 } from "path";
|
|
12411
13586
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
12412
13587
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
12413
13588
|
var REGISTRY_URL = "https://registry.npmjs.org/rush-ai/latest";
|
|
@@ -12430,19 +13605,19 @@ function isNewerVersion(current, latest) {
|
|
|
12430
13605
|
}
|
|
12431
13606
|
async function writeLastCheck(checkFile) {
|
|
12432
13607
|
try {
|
|
12433
|
-
await
|
|
12434
|
-
await
|
|
13608
|
+
await mkdir12(dirname10(checkFile), { recursive: true });
|
|
13609
|
+
await writeFile8(checkFile, JSON.stringify({ lastCheck: Date.now() }));
|
|
12435
13610
|
} catch {
|
|
12436
13611
|
}
|
|
12437
13612
|
}
|
|
12438
13613
|
async function checkForUpdate(currentVersion) {
|
|
12439
|
-
const rushDir =
|
|
12440
|
-
const checkFile =
|
|
13614
|
+
const rushDir = join11(homedir19(), ".rush");
|
|
13615
|
+
const checkFile = join11(rushDir, "update-check.json");
|
|
12441
13616
|
try {
|
|
12442
13617
|
if (process.env.CI) return;
|
|
12443
13618
|
let lastCheck = 0;
|
|
12444
13619
|
try {
|
|
12445
|
-
const data = JSON.parse(await
|
|
13620
|
+
const data = JSON.parse(await readFile15(checkFile, "utf-8"));
|
|
12446
13621
|
lastCheck = data.lastCheck ?? 0;
|
|
12447
13622
|
} catch {
|
|
12448
13623
|
}
|