rush-ai 0.15.0 → 0.16.1
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/AGENTS.md +3 -3
- package/README.md +30 -0
- package/dist/index.js +384 -140
- package/dist/index.js.map +1 -1
- package/dist/skills/.rush-playbooks +1 -0
- package/package.json +3 -2
- package/skills/.rush-playbooks +1 -0
package/dist/index.js
CHANGED
|
@@ -214,7 +214,7 @@ Built-in default agents (see --default):
|
|
|
214
214
|
web-builder site / landing-page builder (returns previewUrl + gitRepoUrl)
|
|
215
215
|
skill-publisher publishes reusable skills back onto the platform
|
|
216
216
|
|
|
217
|
-
For agents: run \`rush-ai
|
|
217
|
+
For agents: run \`rush-ai playbook agent-shelf\` for the full playbook.
|
|
218
218
|
`;
|
|
219
219
|
function registerAgentCommand(program) {
|
|
220
220
|
const agent = program.command("agent").description("Manage and inspect agents").addHelpText("after", AGENT_HELP_AFTER);
|
|
@@ -327,10 +327,9 @@ function registerAgentCommand(program) {
|
|
|
327
327
|
} catch {
|
|
328
328
|
}
|
|
329
329
|
const candidates = parsed?.candidates;
|
|
330
|
-
|
|
330
|
+
if (Array.isArray(candidates) && candidates.length > 0 && candidates.every(
|
|
331
331
|
(c) => typeof c.id === "string" && typeof c.name === "string"
|
|
332
|
-
)
|
|
333
|
-
if (isValid) {
|
|
332
|
+
)) {
|
|
334
333
|
if (format === "json") {
|
|
335
334
|
output.log(JSON.stringify(parsed, null, 2));
|
|
336
335
|
process.exitCode = 1;
|
|
@@ -1410,13 +1409,15 @@ ${MARKER_BEGIN}
|
|
|
1410
1409
|
_rush_cli_completion() {
|
|
1411
1410
|
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
1412
1411
|
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
1413
|
-
local cmds="auth agent task mcp plugin completion config doctor"
|
|
1412
|
+
local cmds="auth agent task skill playbook mcp plugin completion config doctor"
|
|
1414
1413
|
|
|
1415
1414
|
case "$prev" in
|
|
1416
1415
|
rush-ai) COMPREPLY=( $(compgen -W "$cmds" -- "$cur") ) ;;
|
|
1417
1416
|
auth) COMPREPLY=( $(compgen -W "login logout status" -- "$cur") ) ;;
|
|
1418
1417
|
agent) COMPREPLY=( $(compgen -W "list info" -- "$cur") ) ;;
|
|
1419
1418
|
task) COMPREPLY=( $(compgen -W "run create status result list watch cancel" -- "$cur") ) ;;
|
|
1419
|
+
skill) COMPREPLY=( $(compgen -W "init find install list info update outdated uninstall publish group doctor" -- "$cur") ) ;;
|
|
1420
|
+
playbook) COMPREPLY=( $(compgen -W "hand-off agent-shelf" -- "$cur") ) ;;
|
|
1420
1421
|
plugin) COMPREPLY=( $(compgen -W "install list status update uninstall" -- "$cur") ) ;;
|
|
1421
1422
|
completion) COMPREPLY=( $(compgen -W "install bash zsh fish" -- "$cur") ) ;;
|
|
1422
1423
|
config) COMPREPLY=( $(compgen -W "show set use create delete list" -- "$cur") ) ;;
|
|
@@ -1432,6 +1433,8 @@ _rush_cli_completion() {
|
|
|
1432
1433
|
'auth:Authentication commands'
|
|
1433
1434
|
'agent:Agent management'
|
|
1434
1435
|
'task:Task operations'
|
|
1436
|
+
'skill:Skill package management'
|
|
1437
|
+
'playbook:Agent usage playbooks'
|
|
1435
1438
|
'mcp:MCP server'
|
|
1436
1439
|
'plugin:Plugin management'
|
|
1437
1440
|
'completion:Shell completion'
|
|
@@ -1447,6 +1450,8 @@ ${MARKER_BEGIN}
|
|
|
1447
1450
|
complete -c rush-ai -n '__fish_use_subcommand' -a auth -d 'Authentication'
|
|
1448
1451
|
complete -c rush-ai -n '__fish_use_subcommand' -a agent -d 'Agent management'
|
|
1449
1452
|
complete -c rush-ai -n '__fish_use_subcommand' -a task -d 'Task operations'
|
|
1453
|
+
complete -c rush-ai -n '__fish_use_subcommand' -a skill -d 'Skill package management'
|
|
1454
|
+
complete -c rush-ai -n '__fish_use_subcommand' -a playbook -d 'Agent usage playbooks'
|
|
1450
1455
|
complete -c rush-ai -n '__fish_use_subcommand' -a mcp -d 'MCP server'
|
|
1451
1456
|
complete -c rush-ai -n '__fish_use_subcommand' -a plugin -d 'Plugin management'
|
|
1452
1457
|
complete -c rush-ai -n '__fish_use_subcommand' -a completion -d 'Shell completion'
|
|
@@ -1455,6 +1460,8 @@ complete -c rush-ai -n '__fish_use_subcommand' -a doctor -d 'System diagnostics'
|
|
|
1455
1460
|
complete -c rush-ai -n '__fish_seen_subcommand_from auth' -a 'login logout status'
|
|
1456
1461
|
complete -c rush-ai -n '__fish_seen_subcommand_from agent' -a 'list info'
|
|
1457
1462
|
complete -c rush-ai -n '__fish_seen_subcommand_from task' -a 'run create status result list watch cancel'
|
|
1463
|
+
complete -c rush-ai -n '__fish_seen_subcommand_from skill' -a 'init find install list info update outdated uninstall publish group doctor'
|
|
1464
|
+
complete -c rush-ai -n '__fish_seen_subcommand_from playbook' -a 'hand-off agent-shelf'
|
|
1458
1465
|
complete -c rush-ai -n '__fish_seen_subcommand_from plugin' -a 'install list status update uninstall'
|
|
1459
1466
|
complete -c rush-ai -n '__fish_seen_subcommand_from completion' -a 'install bash zsh fish'
|
|
1460
1467
|
complete -c rush-ai -n '__fish_seen_subcommand_from config' -a 'show set use create delete list'
|
|
@@ -1507,7 +1514,8 @@ function registerCompletionCommand(program) {
|
|
|
1507
1514
|
if (shell === "fish") {
|
|
1508
1515
|
try {
|
|
1509
1516
|
await mkdir(dirname(rcFile), { recursive: true });
|
|
1510
|
-
await writeFile(rcFile, script
|
|
1517
|
+
await writeFile(rcFile, `${script}
|
|
1518
|
+
`);
|
|
1511
1519
|
output.success(`Fish completion installed to ${rcFile}`);
|
|
1512
1520
|
} catch {
|
|
1513
1521
|
output.error(`Failed to write to ${rcFile}`);
|
|
@@ -1527,7 +1535,9 @@ function registerCompletionCommand(program) {
|
|
|
1527
1535
|
} catch {
|
|
1528
1536
|
}
|
|
1529
1537
|
try {
|
|
1530
|
-
await appendFile(rcFile,
|
|
1538
|
+
await appendFile(rcFile, `
|
|
1539
|
+
${script}
|
|
1540
|
+
`);
|
|
1531
1541
|
output.success(`Completion installed to ${rcFile}`);
|
|
1532
1542
|
output.dim(
|
|
1533
1543
|
` Run \`source ${rcFile}\` or restart your shell to activate.`
|
|
@@ -4290,9 +4300,80 @@ function registerMcpCommand(program) {
|
|
|
4290
4300
|
});
|
|
4291
4301
|
}
|
|
4292
4302
|
|
|
4303
|
+
// src/commands/playbook/index.ts
|
|
4304
|
+
import { existsSync as existsSync11, readdirSync, readFileSync as readFileSync7 } from "fs";
|
|
4305
|
+
import { basename as basename2, resolve as resolve9 } from "path";
|
|
4306
|
+
var PLAYBOOK_DESCRIPTION = "Print agent usage playbooks (hand-off, agent-shelf, ...) as raw markdown.";
|
|
4307
|
+
var PLAYBOOK_HELP_AFTER = `
|
|
4308
|
+
Examples:
|
|
4309
|
+
$ npx rush-ai playbook Print the playbook index
|
|
4310
|
+
$ npx rush-ai playbook hand-off Print the hand-off playbook
|
|
4311
|
+
$ npx rush-ai playbook agent-shelf Print the agent-shelf playbook
|
|
4312
|
+
$ npx rush-ai playbook --list List available playbooks
|
|
4313
|
+
|
|
4314
|
+
Designed to be consumed by AI agents inside IDEs (Cursor, Claude Code,
|
|
4315
|
+
Codex, etc). Output is raw markdown on stdout \u2014 pipe it, grep it, feed it
|
|
4316
|
+
to your own context.
|
|
4317
|
+
`;
|
|
4318
|
+
function resolvePlaybooksDir() {
|
|
4319
|
+
const baseDir = import.meta.dirname ?? __dirname;
|
|
4320
|
+
if (!baseDir) return null;
|
|
4321
|
+
const candidates = [
|
|
4322
|
+
resolve9(baseDir, "skills"),
|
|
4323
|
+
resolve9(baseDir, "..", "skills"),
|
|
4324
|
+
resolve9(baseDir, "..", "..", "skills"),
|
|
4325
|
+
resolve9(baseDir, "..", "..", "..", "skills"),
|
|
4326
|
+
resolve9(baseDir, "..", "..", "..", "..", "skills")
|
|
4327
|
+
];
|
|
4328
|
+
for (const p of candidates) {
|
|
4329
|
+
if (existsSync11(resolve9(p, ".rush-playbooks"))) return p;
|
|
4330
|
+
}
|
|
4331
|
+
return null;
|
|
4332
|
+
}
|
|
4333
|
+
function listPlaybooks(dir) {
|
|
4334
|
+
return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename2(f, ".md")).sort();
|
|
4335
|
+
}
|
|
4336
|
+
function getAvailablePlaybooks() {
|
|
4337
|
+
const dir = resolvePlaybooksDir();
|
|
4338
|
+
return dir ? listPlaybooks(dir) : [];
|
|
4339
|
+
}
|
|
4340
|
+
function isPlaybookName(name) {
|
|
4341
|
+
if (!name) return false;
|
|
4342
|
+
return getAvailablePlaybooks().includes(name);
|
|
4343
|
+
}
|
|
4344
|
+
function printPlaybook(name, opts = {}) {
|
|
4345
|
+
const dir = resolvePlaybooksDir();
|
|
4346
|
+
if (!dir) {
|
|
4347
|
+
output.error(
|
|
4348
|
+
"Could not locate the skills/ directory. This is a packaging bug \u2014 please file an issue."
|
|
4349
|
+
);
|
|
4350
|
+
process.exit(2);
|
|
4351
|
+
return;
|
|
4352
|
+
}
|
|
4353
|
+
const available = listPlaybooks(dir);
|
|
4354
|
+
if (opts.list) {
|
|
4355
|
+
for (const s of available) output.log(s);
|
|
4356
|
+
return;
|
|
4357
|
+
}
|
|
4358
|
+
const target = name ?? "README";
|
|
4359
|
+
const file2 = resolve9(dir, `${target}.md`);
|
|
4360
|
+
if (!existsSync11(file2)) {
|
|
4361
|
+
output.error(`Unknown playbook "${target}".`);
|
|
4362
|
+
output.dim(`Available: ${available.join(", ") || "(none)"}`);
|
|
4363
|
+
process.exit(1);
|
|
4364
|
+
return;
|
|
4365
|
+
}
|
|
4366
|
+
process.stdout.write(readFileSync7(file2, "utf-8"));
|
|
4367
|
+
}
|
|
4368
|
+
function registerPlaybookCommand(program) {
|
|
4369
|
+
program.command("playbook [name]").description(PLAYBOOK_DESCRIPTION).addHelpText("after", PLAYBOOK_HELP_AFTER).option("--list", "List available playbook names and exit").action((name, opts) => {
|
|
4370
|
+
printPlaybook(name, opts);
|
|
4371
|
+
});
|
|
4372
|
+
}
|
|
4373
|
+
|
|
4293
4374
|
// src/commands/plugin/install.ts
|
|
4294
4375
|
import { rm as rm12 } from "fs/promises";
|
|
4295
|
-
import { resolve as
|
|
4376
|
+
import { resolve as resolve21 } from "path";
|
|
4296
4377
|
|
|
4297
4378
|
// src/installers/claude-code/installer.ts
|
|
4298
4379
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
@@ -4310,7 +4391,7 @@ import {
|
|
|
4310
4391
|
unlink
|
|
4311
4392
|
} from "fs/promises";
|
|
4312
4393
|
import { homedir as homedir5 } from "os";
|
|
4313
|
-
import { dirname as dirname4, join as join9, resolve as
|
|
4394
|
+
import { dirname as dirname4, join as join9, resolve as resolve11 } from "path";
|
|
4314
4395
|
|
|
4315
4396
|
// src/installers/claude-code/mcp.ts
|
|
4316
4397
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
@@ -4362,7 +4443,7 @@ function defaultRushBinaryResolver() {
|
|
|
4362
4443
|
}
|
|
4363
4444
|
|
|
4364
4445
|
// src/installers/claude-code/paths.ts
|
|
4365
|
-
import { resolve as
|
|
4446
|
+
import { resolve as resolve10 } from "path";
|
|
4366
4447
|
var CLAUDE_DIR = ".claude";
|
|
4367
4448
|
var PLUGINS_SUBDIR = "plugins";
|
|
4368
4449
|
var CACHE_SUBDIR = "cache";
|
|
@@ -4380,27 +4461,27 @@ var ClaudeCodePaths = class {
|
|
|
4380
4461
|
}
|
|
4381
4462
|
/** `<home>/.claude/` */
|
|
4382
4463
|
get claudeDir() {
|
|
4383
|
-
return
|
|
4464
|
+
return resolve10(this.home, CLAUDE_DIR);
|
|
4384
4465
|
}
|
|
4385
4466
|
/** `<home>/.claude/settings.json` */
|
|
4386
4467
|
get settingsJson() {
|
|
4387
|
-
return
|
|
4468
|
+
return resolve10(this.claudeDir, "settings.json");
|
|
4388
4469
|
}
|
|
4389
4470
|
/** `<home>/.claude/plugins/` */
|
|
4390
4471
|
get pluginsDir() {
|
|
4391
|
-
return
|
|
4472
|
+
return resolve10(this.claudeDir, PLUGINS_SUBDIR);
|
|
4392
4473
|
}
|
|
4393
4474
|
/** `<home>/.claude/plugins/known_marketplaces.json` */
|
|
4394
4475
|
get knownMarketplacesJson() {
|
|
4395
|
-
return
|
|
4476
|
+
return resolve10(this.pluginsDir, "known_marketplaces.json");
|
|
4396
4477
|
}
|
|
4397
4478
|
/** `<home>/.claude/plugins/installed_plugins.json` */
|
|
4398
4479
|
get installedPluginsJson() {
|
|
4399
|
-
return
|
|
4480
|
+
return resolve10(this.pluginsDir, "installed_plugins.json");
|
|
4400
4481
|
}
|
|
4401
4482
|
/** `<home>/.claude/plugins/cache/` */
|
|
4402
4483
|
get cacheDir() {
|
|
4403
|
-
return
|
|
4484
|
+
return resolve10(this.pluginsDir, CACHE_SUBDIR);
|
|
4404
4485
|
}
|
|
4405
4486
|
/**
|
|
4406
4487
|
* `<home>/.claude/plugins/marketplaces/`。
|
|
@@ -4410,34 +4491,34 @@ var ClaudeCodePaths = class {
|
|
|
4410
4491
|
* Claude Code 会识别不到 plugin 条目(regression fix,bug #4)。
|
|
4411
4492
|
*/
|
|
4412
4493
|
get marketplacesRootDir() {
|
|
4413
|
-
return
|
|
4494
|
+
return resolve10(this.pluginsDir, "marketplaces");
|
|
4414
4495
|
}
|
|
4415
4496
|
/** `<home>/.claude/plugins/marketplaces/<mkt>/` */
|
|
4416
4497
|
marketplaceInstallDir(marketplace) {
|
|
4417
|
-
return
|
|
4498
|
+
return resolve10(this.marketplacesRootDir, marketplace);
|
|
4418
4499
|
}
|
|
4419
4500
|
/** `<home>/.claude/plugins/cache/<mkt>/` */
|
|
4420
4501
|
marketplaceCacheDir(marketplace) {
|
|
4421
|
-
return
|
|
4502
|
+
return resolve10(this.cacheDir, marketplace);
|
|
4422
4503
|
}
|
|
4423
4504
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/` */
|
|
4424
4505
|
pluginCacheDir(ref) {
|
|
4425
|
-
return
|
|
4506
|
+
return resolve10(this.marketplaceCacheDir(ref.marketplace), ref.name);
|
|
4426
4507
|
}
|
|
4427
4508
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/` */
|
|
4428
4509
|
pluginVersionDir(ref, version) {
|
|
4429
|
-
return
|
|
4510
|
+
return resolve10(this.pluginCacheDir(ref), version);
|
|
4430
4511
|
}
|
|
4431
4512
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/.claude-plugin/plugin.json` */
|
|
4432
4513
|
pluginManifestPath(ref, version) {
|
|
4433
|
-
return
|
|
4514
|
+
return resolve10(
|
|
4434
4515
|
this.pluginVersionDir(ref, version),
|
|
4435
4516
|
PLUGIN_MANIFEST_RELATIVE
|
|
4436
4517
|
);
|
|
4437
4518
|
}
|
|
4438
4519
|
/** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/<capability>/` */
|
|
4439
4520
|
capabilityDir(ref, version, capability) {
|
|
4440
|
-
return
|
|
4521
|
+
return resolve10(this.pluginVersionDir(ref, version), capability);
|
|
4441
4522
|
}
|
|
4442
4523
|
};
|
|
4443
4524
|
function pluginKey(ref) {
|
|
@@ -4554,7 +4635,7 @@ var ClaudeCodeInstaller = class {
|
|
|
4554
4635
|
await mkdir5(pluginVersionDir, { recursive: true });
|
|
4555
4636
|
writtenFiles.push(pluginVersionDir);
|
|
4556
4637
|
for (const cap of CAPABILITY_DIRS) {
|
|
4557
|
-
const srcDir =
|
|
4638
|
+
const srcDir = resolve11(plugin.sourceDir, cap);
|
|
4558
4639
|
if (!await dirExists(srcDir)) continue;
|
|
4559
4640
|
const dstDir = this.paths.capabilityDir(ref, version, cap);
|
|
4560
4641
|
await mkdir5(dstDir, { recursive: true });
|
|
@@ -4568,6 +4649,11 @@ var ClaudeCodeInstaller = class {
|
|
|
4568
4649
|
plugin.manifest,
|
|
4569
4650
|
this.rushBinaryResolver
|
|
4570
4651
|
);
|
|
4652
|
+
if (normalizedMcp && Object.keys(normalizedMcp).length > 0) {
|
|
4653
|
+
const mcpJsonPath = resolve11(pluginVersionDir, ".mcp.json");
|
|
4654
|
+
await writeJsonFile(mcpJsonPath, { mcpServers: normalizedMcp });
|
|
4655
|
+
writtenFiles.push(mcpJsonPath);
|
|
4656
|
+
}
|
|
4571
4657
|
const pluginJsonContent = buildPluginJson(plugin, normalizedMcp);
|
|
4572
4658
|
await writeJsonFile(manifestPath, pluginJsonContent);
|
|
4573
4659
|
writtenFiles.push(manifestPath);
|
|
@@ -4768,8 +4854,8 @@ var ClaudeCodeInstaller = class {
|
|
|
4768
4854
|
const kind = await inodeKind(mktDir);
|
|
4769
4855
|
if (kind === "symlink") {
|
|
4770
4856
|
const target = await readlink(mktDir).catch(() => null);
|
|
4771
|
-
const resolvedTarget = target !== null ?
|
|
4772
|
-
if (resolvedTarget && resolvedTarget ===
|
|
4857
|
+
const resolvedTarget = target !== null ? resolve11(dirname4(mktDir), target) : null;
|
|
4858
|
+
if (resolvedTarget && resolvedTarget === resolve11(src.rootDir)) {
|
|
4773
4859
|
return { written: false, path: mktDir };
|
|
4774
4860
|
}
|
|
4775
4861
|
throw new Error(
|
|
@@ -4838,7 +4924,7 @@ var ClaudeCodeInstaller = class {
|
|
|
4838
4924
|
const pluginVersionDir = this.paths.pluginVersionDir(ref, version);
|
|
4839
4925
|
const dryFiles = [pluginVersionDir];
|
|
4840
4926
|
for (const cap of CAPABILITY_DIRS) {
|
|
4841
|
-
const srcDir =
|
|
4927
|
+
const srcDir = resolve11(plugin.sourceDir, cap);
|
|
4842
4928
|
if (!await dirExists(srcDir)) continue;
|
|
4843
4929
|
const dstDir = this.paths.capabilityDir(ref, version, cap);
|
|
4844
4930
|
dryFiles.push(dstDir);
|
|
@@ -4846,6 +4932,14 @@ var ClaudeCodeInstaller = class {
|
|
|
4846
4932
|
dryFiles.push(...plannedFiles);
|
|
4847
4933
|
}
|
|
4848
4934
|
dryFiles.push(this.paths.pluginManifestPath(ref, version));
|
|
4935
|
+
const normalizedMcpDry = normalizeClaudeMcpServers(
|
|
4936
|
+
ref,
|
|
4937
|
+
plugin.manifest,
|
|
4938
|
+
this.rushBinaryResolver
|
|
4939
|
+
);
|
|
4940
|
+
if (normalizedMcpDry && Object.keys(normalizedMcpDry).length > 0) {
|
|
4941
|
+
dryFiles.push(resolve11(pluginVersionDir, ".mcp.json"));
|
|
4942
|
+
}
|
|
4849
4943
|
if (this.marketplaceSource) {
|
|
4850
4944
|
const mktInstallDir = this.paths.marketplaceInstallDir(ref.marketplace);
|
|
4851
4945
|
if (await inodeKind(mktInstallDir) === "missing") {
|
|
@@ -5293,6 +5387,19 @@ var ClaudeCodeInstaller = class {
|
|
|
5293
5387
|
() => ({})
|
|
5294
5388
|
);
|
|
5295
5389
|
const servers = data.mcpServers;
|
|
5390
|
+
if (typeof servers === "string") {
|
|
5391
|
+
const mcpJsonPath = resolve11(dirname4(manifestPath), "..", servers);
|
|
5392
|
+
if (!await pathExists(mcpJsonPath)) return [];
|
|
5393
|
+
const { data: mcpData } = await readJsonFile(
|
|
5394
|
+
mcpJsonPath,
|
|
5395
|
+
() => ({})
|
|
5396
|
+
);
|
|
5397
|
+
const mcpServers = mcpData.mcpServers;
|
|
5398
|
+
if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) {
|
|
5399
|
+
return [];
|
|
5400
|
+
}
|
|
5401
|
+
return Object.keys(mcpServers);
|
|
5402
|
+
}
|
|
5296
5403
|
if (!servers || typeof servers !== "object" || Array.isArray(servers)) {
|
|
5297
5404
|
return [];
|
|
5298
5405
|
}
|
|
@@ -5336,7 +5443,7 @@ function buildPluginJson(plugin, mcpServers) {
|
|
|
5336
5443
|
} = manifest;
|
|
5337
5444
|
const out = { ...rest };
|
|
5338
5445
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
5339
|
-
out.mcpServers =
|
|
5446
|
+
out.mcpServers = "./.mcp.json";
|
|
5340
5447
|
}
|
|
5341
5448
|
return out;
|
|
5342
5449
|
}
|
|
@@ -5471,11 +5578,11 @@ import {
|
|
|
5471
5578
|
} from "fs/promises";
|
|
5472
5579
|
import { homedir as homedir6 } from "os";
|
|
5473
5580
|
import {
|
|
5474
|
-
basename as
|
|
5581
|
+
basename as basename3,
|
|
5475
5582
|
dirname as dirname6,
|
|
5476
5583
|
extname,
|
|
5477
5584
|
relative as pathRelative,
|
|
5478
|
-
resolve as
|
|
5585
|
+
resolve as resolve12
|
|
5479
5586
|
} from "path";
|
|
5480
5587
|
|
|
5481
5588
|
// src/installers/codex/mcp.ts
|
|
@@ -6116,10 +6223,10 @@ var CodexInstaller = class {
|
|
|
6116
6223
|
const srcMcp = plugin.manifest.mcpServers;
|
|
6117
6224
|
if (srcMcp !== void 0 && srcMcp !== null) {
|
|
6118
6225
|
if (typeof srcMcp === "string") {
|
|
6119
|
-
const destPath =
|
|
6226
|
+
const destPath = resolve12(versionDir, srcMcp);
|
|
6120
6227
|
files.push(destPath);
|
|
6121
|
-
const srcPath =
|
|
6122
|
-
const rel = pathRelative(
|
|
6228
|
+
const srcPath = resolve12(plugin.sourceDir, srcMcp);
|
|
6229
|
+
const rel = pathRelative(resolve12(plugin.sourceDir), srcPath);
|
|
6123
6230
|
const traversal = rel === ".." || rel.startsWith("..") || rel.startsWith("/");
|
|
6124
6231
|
const exists = await pathExists5(srcPath);
|
|
6125
6232
|
if (traversal || !exists) {
|
|
@@ -6264,13 +6371,13 @@ function assertSafePathComponents(ref, version) {
|
|
|
6264
6371
|
}
|
|
6265
6372
|
async function copyAuthorProvidedMcp(sourceDir, versionDir, relativeRef) {
|
|
6266
6373
|
if (typeof relativeRef !== "string" || relativeRef.length === 0) return null;
|
|
6267
|
-
const srcPath =
|
|
6268
|
-
const rel = pathRelative(
|
|
6374
|
+
const srcPath = resolve12(sourceDir, relativeRef);
|
|
6375
|
+
const rel = pathRelative(resolve12(sourceDir), srcPath);
|
|
6269
6376
|
if (rel === ".." || rel.startsWith("..") || rel.startsWith("/")) {
|
|
6270
6377
|
return null;
|
|
6271
6378
|
}
|
|
6272
6379
|
if (!await pathExists5(srcPath)) return null;
|
|
6273
|
-
const destPath =
|
|
6380
|
+
const destPath = resolve12(versionDir, relativeRef);
|
|
6274
6381
|
await mkdir7(dirname6(destPath), { recursive: true });
|
|
6275
6382
|
const raw = await readFile7(srcPath, "utf8");
|
|
6276
6383
|
await writeFileAtomic(destPath, raw);
|
|
@@ -6287,13 +6394,13 @@ async function copyAuthorProvidedMcp(sourceDir, versionDir, relativeRef) {
|
|
|
6287
6394
|
}
|
|
6288
6395
|
async function writeCodexMarketplaceMirror(input) {
|
|
6289
6396
|
const pluginParent = dirname6(input.pluginDir);
|
|
6290
|
-
const pluginBase =
|
|
6397
|
+
const pluginBase = basename3(input.pluginDir);
|
|
6291
6398
|
await mkdir7(pluginParent, { recursive: true });
|
|
6292
6399
|
const tmpPluginDir = await mkdtemp(
|
|
6293
|
-
|
|
6400
|
+
resolve12(pluginParent, `.${pluginBase}.tmp-`)
|
|
6294
6401
|
);
|
|
6295
6402
|
const backupPluginDir = await mkdtemp(
|
|
6296
|
-
|
|
6403
|
+
resolve12(pluginParent, `.${pluginBase}.bak-`)
|
|
6297
6404
|
);
|
|
6298
6405
|
let hadExistingPluginDir = false;
|
|
6299
6406
|
let swappedPluginDir = false;
|
|
@@ -6326,7 +6433,7 @@ async function writeCodexMarketplaceMirror(input) {
|
|
|
6326
6433
|
try {
|
|
6327
6434
|
if (await pathExists5(input.pluginDir)) {
|
|
6328
6435
|
displacedPluginDir = await mkdtemp(
|
|
6329
|
-
|
|
6436
|
+
resolve12(pluginParent, `.${pluginBase}.failed-`)
|
|
6330
6437
|
);
|
|
6331
6438
|
await rm7(displacedPluginDir, { recursive: true, force: true });
|
|
6332
6439
|
await rename6(input.pluginDir, displacedPluginDir);
|
|
@@ -6413,7 +6520,7 @@ async function planCodexSkills(plugin) {
|
|
|
6413
6520
|
const usedNames = /* @__PURE__ */ new Set();
|
|
6414
6521
|
const coveredAliases = /* @__PURE__ */ new Set();
|
|
6415
6522
|
const commandFiles = await listMarkdownFiles(
|
|
6416
|
-
|
|
6523
|
+
resolve12(plugin.sourceDir, "commands"),
|
|
6417
6524
|
{
|
|
6418
6525
|
recursive: false
|
|
6419
6526
|
}
|
|
@@ -6423,19 +6530,19 @@ async function planCodexSkills(plugin) {
|
|
|
6423
6530
|
commandFiles.map((file2) => markdownSkillAlias(plugin.manifest.name, file2))
|
|
6424
6531
|
)
|
|
6425
6532
|
);
|
|
6426
|
-
const nativeSkillsDir =
|
|
6533
|
+
const nativeSkillsDir = resolve12(plugin.sourceDir, "skills");
|
|
6427
6534
|
if (await hasCodexSkillDir(nativeSkillsDir)) {
|
|
6428
6535
|
for (const name of await listCodexSkillNames(nativeSkillsDir)) {
|
|
6429
6536
|
usedNames.add(name);
|
|
6430
6537
|
coveredAliases.add(skillAlias(plugin.manifest.name, name));
|
|
6431
6538
|
sources.push({
|
|
6432
6539
|
kind: "copy-skill",
|
|
6433
|
-
sourceDir:
|
|
6540
|
+
sourceDir: resolve12(nativeSkillsDir, name),
|
|
6434
6541
|
skillName: name
|
|
6435
6542
|
});
|
|
6436
6543
|
}
|
|
6437
6544
|
} else {
|
|
6438
|
-
const dotSkillsDir =
|
|
6545
|
+
const dotSkillsDir = resolve12(plugin.sourceDir, ".skills");
|
|
6439
6546
|
const claudeSkills = await listMarkdownFiles(dotSkillsDir);
|
|
6440
6547
|
const claudeAliases = /* @__PURE__ */ new Map();
|
|
6441
6548
|
for (const file2 of claudeSkills) {
|
|
@@ -6474,7 +6581,7 @@ async function planCodexSkills(plugin) {
|
|
|
6474
6581
|
async function materializeCodexSkills(plan, dstSkills) {
|
|
6475
6582
|
for (const source of plan.sources) {
|
|
6476
6583
|
if (source.kind === "copy-skill") {
|
|
6477
|
-
await cp(source.sourceDir,
|
|
6584
|
+
await cp(source.sourceDir, resolve12(dstSkills, source.skillName), {
|
|
6478
6585
|
recursive: true,
|
|
6479
6586
|
dereference: false,
|
|
6480
6587
|
preserveTimestamps: true,
|
|
@@ -6485,7 +6592,7 @@ async function materializeCodexSkills(plan, dstSkills) {
|
|
|
6485
6592
|
const raw = await readFile7(source.sourceFile, "utf8");
|
|
6486
6593
|
const normalized = normalizeSkillMarkdown(raw, source.skillName);
|
|
6487
6594
|
await writeFileAtomic(
|
|
6488
|
-
|
|
6595
|
+
resolve12(dstSkills, source.skillName, "SKILL.md"),
|
|
6489
6596
|
normalized
|
|
6490
6597
|
);
|
|
6491
6598
|
}
|
|
@@ -6571,7 +6678,7 @@ async function listCodexSkillNames(skillsDir) {
|
|
|
6571
6678
|
const names = [];
|
|
6572
6679
|
for (const entry of entries) {
|
|
6573
6680
|
if (!entry.isDirectory()) continue;
|
|
6574
|
-
if (await pathExists5(
|
|
6681
|
+
if (await pathExists5(resolve12(skillsDir, entry.name, "SKILL.md"))) {
|
|
6575
6682
|
names.push(entry.name);
|
|
6576
6683
|
}
|
|
6577
6684
|
}
|
|
@@ -6589,7 +6696,7 @@ async function listMarkdownFiles(dir, opts = {}) {
|
|
|
6589
6696
|
return;
|
|
6590
6697
|
}
|
|
6591
6698
|
for (const entry of entries) {
|
|
6592
|
-
const abs =
|
|
6699
|
+
const abs = resolve12(current, entry.name);
|
|
6593
6700
|
if (entry.isDirectory()) {
|
|
6594
6701
|
if (recursive) await walk2(abs);
|
|
6595
6702
|
continue;
|
|
@@ -6617,7 +6724,7 @@ async function markdownSkillAlias(pluginName, filePath) {
|
|
|
6617
6724
|
const triggerAlias = extractTriggerAlias(await readFile7(filePath, "utf8"));
|
|
6618
6725
|
return skillAlias(
|
|
6619
6726
|
pluginName,
|
|
6620
|
-
triggerAlias ??
|
|
6727
|
+
triggerAlias ?? basename3(filePath, extname(filePath))
|
|
6621
6728
|
);
|
|
6622
6729
|
}
|
|
6623
6730
|
function markdownPathAlias(pluginName, baseDir, filePath) {
|
|
@@ -6681,7 +6788,7 @@ function parsePluginKey2(key) {
|
|
|
6681
6788
|
return { name: key.slice(0, at), marketplace: key.slice(at + 1) };
|
|
6682
6789
|
}
|
|
6683
6790
|
async function detectInstalledVersion(home, ref) {
|
|
6684
|
-
const baseDir =
|
|
6791
|
+
const baseDir = resolve12(
|
|
6685
6792
|
codexHomeDir(home),
|
|
6686
6793
|
"plugins",
|
|
6687
6794
|
"cache",
|
|
@@ -6697,8 +6804,8 @@ async function detectInstalledVersion(home, ref) {
|
|
|
6697
6804
|
}
|
|
6698
6805
|
let best = null;
|
|
6699
6806
|
for (const entry of entries) {
|
|
6700
|
-
const versionDir =
|
|
6701
|
-
const manifestPath =
|
|
6807
|
+
const versionDir = resolve12(baseDir, entry);
|
|
6808
|
+
const manifestPath = resolve12(versionDir, ".codex-plugin", "plugin.json");
|
|
6702
6809
|
if (!await pathExists5(manifestPath)) continue;
|
|
6703
6810
|
try {
|
|
6704
6811
|
const raw = await readFile7(manifestPath, "utf8");
|
|
@@ -6739,7 +6846,7 @@ import {
|
|
|
6739
6846
|
writeFile as writeFile8
|
|
6740
6847
|
} from "fs/promises";
|
|
6741
6848
|
import { homedir as homedir7 } from "os";
|
|
6742
|
-
import { dirname as dirname8, relative, resolve as
|
|
6849
|
+
import { dirname as dirname8, relative, resolve as resolve15 } from "path";
|
|
6743
6850
|
|
|
6744
6851
|
// src/installers/cursor/marker.ts
|
|
6745
6852
|
var RUSH_AI_MARKER = "<!-- rush-ai:auto-generated -->";
|
|
@@ -6858,25 +6965,25 @@ async function pathExists6(p) {
|
|
|
6858
6965
|
}
|
|
6859
6966
|
|
|
6860
6967
|
// src/installers/cursor/paths.ts
|
|
6861
|
-
import { resolve as
|
|
6968
|
+
import { resolve as resolve13 } from "path";
|
|
6862
6969
|
var CURSOR_RELATIVE_DIR = ".cursor";
|
|
6863
6970
|
function cursorDir(home) {
|
|
6864
|
-
return
|
|
6971
|
+
return resolve13(home, CURSOR_RELATIVE_DIR);
|
|
6865
6972
|
}
|
|
6866
6973
|
function cursorMcpJsonPath(home) {
|
|
6867
|
-
return
|
|
6974
|
+
return resolve13(cursorDir(home), "mcp.json");
|
|
6868
6975
|
}
|
|
6869
6976
|
function cursorRulesDir(home) {
|
|
6870
|
-
return
|
|
6977
|
+
return resolve13(cursorDir(home), "rules");
|
|
6871
6978
|
}
|
|
6872
6979
|
function cursorRuleMdcPath(home, ruleName) {
|
|
6873
|
-
return
|
|
6980
|
+
return resolve13(cursorRulesDir(home), `${ruleName}.mdc`);
|
|
6874
6981
|
}
|
|
6875
6982
|
function cursorSkillsDir(home) {
|
|
6876
|
-
return
|
|
6983
|
+
return resolve13(cursorDir(home), "skills");
|
|
6877
6984
|
}
|
|
6878
6985
|
function cursorSkillDir(home, skillName) {
|
|
6879
|
-
return
|
|
6986
|
+
return resolve13(cursorSkillsDir(home), skillName);
|
|
6880
6987
|
}
|
|
6881
6988
|
function bridgeSkillFileReference(skillName) {
|
|
6882
6989
|
return `.cursor/skills/${skillName}/SKILL.md`;
|
|
@@ -6987,9 +7094,9 @@ function quoteIfNeeded(value) {
|
|
|
6987
7094
|
|
|
6988
7095
|
// src/installers/cursor/skills.ts
|
|
6989
7096
|
import { readFile as readFile9 } from "fs/promises";
|
|
6990
|
-
import { resolve as
|
|
7097
|
+
import { resolve as resolve14 } from "path";
|
|
6991
7098
|
async function readSkillDescription(skillSourceDir) {
|
|
6992
|
-
const skillMdPath =
|
|
7099
|
+
const skillMdPath = resolve14(skillSourceDir, "SKILL.md");
|
|
6993
7100
|
let raw;
|
|
6994
7101
|
try {
|
|
6995
7102
|
raw = await readFile9(skillMdPath, "utf8");
|
|
@@ -7352,10 +7459,10 @@ var CursorInstaller = class {
|
|
|
7352
7459
|
const prevEntry = registryStore.get(plugin.ref);
|
|
7353
7460
|
const prevFiles = new Set(prevEntry?.targets?.cursor?.files ?? []);
|
|
7354
7461
|
if (plugin.capabilities.includes("skills")) {
|
|
7355
|
-
const skillsSourceDir =
|
|
7462
|
+
const skillsSourceDir = resolve15(plugin.sourceDir, "skills");
|
|
7356
7463
|
const skillDirs = await listSkillDirs(skillsSourceDir);
|
|
7357
7464
|
for (const skillName of skillDirs) {
|
|
7358
|
-
const srcDir =
|
|
7465
|
+
const srcDir = resolve15(skillsSourceDir, skillName);
|
|
7359
7466
|
const destDir = cursorSkillDir(this.home, skillName);
|
|
7360
7467
|
const bridgeRulePath = cursorRuleMdcPath(this.home, skillName);
|
|
7361
7468
|
if (await dirExists2(destDir)) {
|
|
@@ -7387,10 +7494,10 @@ var CursorInstaller = class {
|
|
|
7387
7494
|
}
|
|
7388
7495
|
}
|
|
7389
7496
|
if (plugin.capabilities.includes("rules")) {
|
|
7390
|
-
const rulesSourceDir =
|
|
7497
|
+
const rulesSourceDir = resolve15(plugin.sourceDir, "rules");
|
|
7391
7498
|
const rulesFiles = await listRuleMdFiles(rulesSourceDir);
|
|
7392
7499
|
for (const ruleFile of rulesFiles) {
|
|
7393
|
-
const srcPath =
|
|
7500
|
+
const srcPath = resolve15(rulesSourceDir, ruleFile);
|
|
7394
7501
|
const ruleName = ruleFile.replace(/\.md$/i, "");
|
|
7395
7502
|
const destPath = cursorRuleMdcPath(this.home, ruleName);
|
|
7396
7503
|
await assertMdcWritable(destPath, args.force);
|
|
@@ -7613,12 +7720,12 @@ async function classifyArtifactPath(p) {
|
|
|
7613
7720
|
// src/migration/cleanup.ts
|
|
7614
7721
|
import { lstat as lstat3, rm as rm10, unlink as unlink2 } from "fs/promises";
|
|
7615
7722
|
import { homedir as homedir9 } from "os";
|
|
7616
|
-
import { resolve as
|
|
7723
|
+
import { resolve as resolve17 } from "path";
|
|
7617
7724
|
|
|
7618
7725
|
// src/migration/detect.ts
|
|
7619
7726
|
import { access as access9, lstat as lstat2, readFile as readFile11, readlink as readlink2, stat as stat8 } from "fs/promises";
|
|
7620
7727
|
import { homedir as homedir8 } from "os";
|
|
7621
|
-
import { isAbsolute as isAbsolute4, resolve as
|
|
7728
|
+
import { isAbsolute as isAbsolute4, resolve as resolve16 } from "path";
|
|
7622
7729
|
var LEGACY_ASSET_MANIFEST_RELATIVE = ".rush/plugins/claude-code/asset-manifest.json";
|
|
7623
7730
|
var LEGACY_ASSETS_DIR_RELATIVE = ".rush/plugins/claude-code/assets";
|
|
7624
7731
|
var LEGACY_CLAUDE_CODE_DIR_RELATIVE = ".rush/plugins/claude-code";
|
|
@@ -7637,7 +7744,7 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
|
|
|
7637
7744
|
const lst = await lstat2(linkPath);
|
|
7638
7745
|
if (!lst.isSymbolicLink()) return false;
|
|
7639
7746
|
const rawTarget = await readlink2(linkPath);
|
|
7640
|
-
const resolvedTarget = isAbsolute4(rawTarget) ? rawTarget :
|
|
7747
|
+
const resolvedTarget = isAbsolute4(rawTarget) ? rawTarget : resolve16(linkPath, "..", rawTarget);
|
|
7641
7748
|
return resolvedTarget === expectedTarget || resolvedTarget.startsWith(`${expectedTarget}/`);
|
|
7642
7749
|
} catch {
|
|
7643
7750
|
return false;
|
|
@@ -7674,10 +7781,10 @@ async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
|
|
|
7674
7781
|
}
|
|
7675
7782
|
async function detectLegacyInstall(opts = {}) {
|
|
7676
7783
|
const home = opts.home ?? homedir8();
|
|
7677
|
-
const assetManifestPath =
|
|
7678
|
-
const legacyAssetsDir =
|
|
7679
|
-
const skillSymlinkPath =
|
|
7680
|
-
const settingsJsonPath =
|
|
7784
|
+
const assetManifestPath = resolve16(home, LEGACY_ASSET_MANIFEST_RELATIVE);
|
|
7785
|
+
const legacyAssetsDir = resolve16(home, LEGACY_ASSETS_DIR_RELATIVE);
|
|
7786
|
+
const skillSymlinkPath = resolve16(home, LEGACY_SKILL_SYMLINK_RELATIVE);
|
|
7787
|
+
const settingsJsonPath = resolve16(home, CLAUDE_SETTINGS_JSON_RELATIVE);
|
|
7681
7788
|
const [hasAssetManifest, hasLegacySkillSymlink, legacyMcpWithoutEnabled] = await Promise.all([
|
|
7682
7789
|
pathExists7(assetManifestPath),
|
|
7683
7790
|
isSymlinkPointingInto(skillSymlinkPath, legacyAssetsDir),
|
|
@@ -7692,7 +7799,7 @@ async function detectLegacyInstall(opts = {}) {
|
|
|
7692
7799
|
}
|
|
7693
7800
|
async function detectLegacyVersion(opts = {}) {
|
|
7694
7801
|
const home = opts.home ?? homedir8();
|
|
7695
|
-
const manifestPath =
|
|
7802
|
+
const manifestPath = resolve16(home, LEGACY_ASSET_MANIFEST_RELATIVE);
|
|
7696
7803
|
try {
|
|
7697
7804
|
const lst = await stat8(manifestPath);
|
|
7698
7805
|
if (!lst.isFile()) return "0.7.x";
|
|
@@ -7710,7 +7817,7 @@ async function detectLegacyVersion(opts = {}) {
|
|
|
7710
7817
|
|
|
7711
7818
|
// src/migration/cleanup.ts
|
|
7712
7819
|
async function cleanupLegacyDir(home) {
|
|
7713
|
-
const dir =
|
|
7820
|
+
const dir = resolve17(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
|
|
7714
7821
|
try {
|
|
7715
7822
|
await rm10(dir, { recursive: true, force: true });
|
|
7716
7823
|
return { ok: true };
|
|
@@ -7722,7 +7829,7 @@ async function cleanupLegacyDir(home) {
|
|
|
7722
7829
|
}
|
|
7723
7830
|
}
|
|
7724
7831
|
async function cleanupLegacySymlink(home) {
|
|
7725
|
-
const path4 =
|
|
7832
|
+
const path4 = resolve17(home, LEGACY_SKILL_SYMLINK_RELATIVE);
|
|
7726
7833
|
try {
|
|
7727
7834
|
let lst;
|
|
7728
7835
|
try {
|
|
@@ -7744,7 +7851,7 @@ async function cleanupLegacySymlink(home) {
|
|
|
7744
7851
|
}
|
|
7745
7852
|
}
|
|
7746
7853
|
async function cleanupLegacyMcp(home) {
|
|
7747
|
-
const path4 =
|
|
7854
|
+
const path4 = resolve17(home, CLAUDE_SETTINGS_JSON_RELATIVE);
|
|
7748
7855
|
try {
|
|
7749
7856
|
const { data, existed } = await readJsonFile(
|
|
7750
7857
|
path4,
|
|
@@ -7802,11 +7909,11 @@ function errorMessage(err) {
|
|
|
7802
7909
|
// src/migration/log.ts
|
|
7803
7910
|
import { appendFile as appendFile2, mkdir as mkdir10 } from "fs/promises";
|
|
7804
7911
|
import { homedir as homedir10 } from "os";
|
|
7805
|
-
import { dirname as dirname9, resolve as
|
|
7912
|
+
import { dirname as dirname9, resolve as resolve18 } from "path";
|
|
7806
7913
|
var MIGRATION_LOG_RELATIVE_PATH = ".rush/plugins/migration-failed.log";
|
|
7807
7914
|
function resolveMigrationLogPath(opts = {}) {
|
|
7808
7915
|
const home = opts.home ?? homedir10();
|
|
7809
|
-
return
|
|
7916
|
+
return resolve18(home, MIGRATION_LOG_RELATIVE_PATH);
|
|
7810
7917
|
}
|
|
7811
7918
|
async function appendMigrationFailure(reason, opts = {}) {
|
|
7812
7919
|
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -7847,7 +7954,7 @@ import {
|
|
|
7847
7954
|
// src/plugins/capabilities.ts
|
|
7848
7955
|
import { constants as fsConstants9 } from "fs";
|
|
7849
7956
|
import { access as access10, readdir as readdir4, stat as stat9 } from "fs/promises";
|
|
7850
|
-
import { extname as extname2, resolve as
|
|
7957
|
+
import { extname as extname2, resolve as resolve19 } from "path";
|
|
7851
7958
|
var CAPABILITY_ORDER = [
|
|
7852
7959
|
"commands",
|
|
7853
7960
|
"skills",
|
|
@@ -7876,10 +7983,10 @@ async function scanCapabilities(sourceDir, manifest) {
|
|
|
7876
7983
|
return CAPABILITY_ORDER.filter((cap) => found.has(cap));
|
|
7877
7984
|
}
|
|
7878
7985
|
async function hasCommands(sourceDir) {
|
|
7879
|
-
return dirHasFileWithExt(
|
|
7986
|
+
return dirHasFileWithExt(resolve19(sourceDir, "commands"), ".md");
|
|
7880
7987
|
}
|
|
7881
7988
|
async function hasSkills(sourceDir) {
|
|
7882
|
-
const skillsDir =
|
|
7989
|
+
const skillsDir = resolve19(sourceDir, "skills");
|
|
7883
7990
|
if (await dirExists3(skillsDir)) {
|
|
7884
7991
|
let entries;
|
|
7885
7992
|
try {
|
|
@@ -7889,7 +7996,7 @@ async function hasSkills(sourceDir) {
|
|
|
7889
7996
|
}
|
|
7890
7997
|
for (const entry of entries) {
|
|
7891
7998
|
if (!entry.isDirectory()) continue;
|
|
7892
|
-
const skillMdPath =
|
|
7999
|
+
const skillMdPath = resolve19(skillsDir, entry.name, "SKILL.md");
|
|
7893
8000
|
if (await fileExists2(skillMdPath)) {
|
|
7894
8001
|
return true;
|
|
7895
8002
|
}
|
|
@@ -7898,10 +8005,10 @@ async function hasSkills(sourceDir) {
|
|
|
7898
8005
|
return hasClaudeStyleSkills(sourceDir);
|
|
7899
8006
|
}
|
|
7900
8007
|
async function hasRules(sourceDir) {
|
|
7901
|
-
return dirHasFileWithExt(
|
|
8008
|
+
return dirHasFileWithExt(resolve19(sourceDir, "rules"), ".md");
|
|
7902
8009
|
}
|
|
7903
8010
|
async function hasHooks(sourceDir) {
|
|
7904
|
-
const hooksDir =
|
|
8011
|
+
const hooksDir = resolve19(sourceDir, "hooks");
|
|
7905
8012
|
if (!await dirExists3(hooksDir)) {
|
|
7906
8013
|
return false;
|
|
7907
8014
|
}
|
|
@@ -7943,7 +8050,7 @@ async function dirHasFileWithExt(dirPath, ext) {
|
|
|
7943
8050
|
);
|
|
7944
8051
|
}
|
|
7945
8052
|
async function hasClaudeStyleSkills(sourceDir) {
|
|
7946
|
-
const dotSkillsDir =
|
|
8053
|
+
const dotSkillsDir = resolve19(sourceDir, ".skills");
|
|
7947
8054
|
if (!await dirExists3(dotSkillsDir)) {
|
|
7948
8055
|
return false;
|
|
7949
8056
|
}
|
|
@@ -7955,7 +8062,7 @@ async function hasClaudeStyleSkills(sourceDir) {
|
|
|
7955
8062
|
return false;
|
|
7956
8063
|
}
|
|
7957
8064
|
for (const entry of entries) {
|
|
7958
|
-
const abs =
|
|
8065
|
+
const abs = resolve19(dirPath, entry.name);
|
|
7959
8066
|
if (entry.isDirectory()) {
|
|
7960
8067
|
if (await walk2(abs)) return true;
|
|
7961
8068
|
continue;
|
|
@@ -8050,10 +8157,10 @@ var PluginCloneFailedError = class extends PluginResolverError {
|
|
|
8050
8157
|
// src/plugins/manifest.ts
|
|
8051
8158
|
import { constants as fsConstants10 } from "fs";
|
|
8052
8159
|
import { access as access11, readFile as readFile12 } from "fs/promises";
|
|
8053
|
-
import { resolve as
|
|
8160
|
+
import { resolve as resolve20 } from "path";
|
|
8054
8161
|
var PLUGIN_MANIFEST_RELATIVE_PATH = ".claude-plugin/plugin.json";
|
|
8055
8162
|
async function readPluginManifest(pluginName, sourceDir) {
|
|
8056
|
-
const manifestPath =
|
|
8163
|
+
const manifestPath = resolve20(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
|
|
8057
8164
|
try {
|
|
8058
8165
|
await access11(manifestPath, fsConstants10.R_OK);
|
|
8059
8166
|
} catch {
|
|
@@ -8951,8 +9058,8 @@ async function runInstall(input) {
|
|
|
8951
9058
|
}
|
|
8952
9059
|
}
|
|
8953
9060
|
if (marketplace.source.kind === "rush") {
|
|
8954
|
-
const pluginDir =
|
|
8955
|
-
const manifestPath =
|
|
9061
|
+
const pluginDir = resolve21(marketplace.rootDir, "plugins", ref.name);
|
|
9062
|
+
const manifestPath = resolve21(pluginDir, ".claude-plugin/plugin.json");
|
|
8956
9063
|
const alreadyMaterialized = await pathExists2(manifestPath);
|
|
8957
9064
|
const hasNewSecrets = input.secrets && Object.keys(input.secrets).length > 0;
|
|
8958
9065
|
const shouldMaterialize = !alreadyMaterialized || force || hasNewSecrets;
|
|
@@ -9831,62 +9938,195 @@ function registerPluginCommand(program) {
|
|
|
9831
9938
|
}
|
|
9832
9939
|
|
|
9833
9940
|
// src/commands/skill/index.ts
|
|
9834
|
-
import {
|
|
9835
|
-
import {
|
|
9836
|
-
var SKILL_DESCRIPTION = "
|
|
9941
|
+
import { spawn as spawn2 } from "child_process";
|
|
9942
|
+
import { fileURLToPath } from "url";
|
|
9943
|
+
var SKILL_DESCRIPTION = "Manage AI agent skills through reskill using Rush auth and registry defaults.";
|
|
9837
9944
|
var SKILL_HELP_AFTER = `
|
|
9838
9945
|
Examples:
|
|
9839
|
-
$
|
|
9840
|
-
$
|
|
9841
|
-
$
|
|
9842
|
-
$
|
|
9946
|
+
$ rush-ai skill install @kanyun/rush-find-skills
|
|
9947
|
+
$ rush-ai skill find vue
|
|
9948
|
+
$ rush-ai skill list
|
|
9949
|
+
$ rush-ai skill publish --dry-run
|
|
9950
|
+
$ rush-ai skill group list
|
|
9843
9951
|
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9952
|
+
Auth:
|
|
9953
|
+
Use rush-ai auth login/logout/status. rush-ai skill forwards your Rush
|
|
9954
|
+
token to the bundled reskill package manager via RESKILL_TOKEN and
|
|
9955
|
+
defaults RESKILL_REGISTRY to the active Rush API URL.
|
|
9956
|
+
|
|
9957
|
+
Notes:
|
|
9958
|
+
rush-ai skill is a thin facade: unknown reskill flags and arguments are
|
|
9959
|
+
forwarded as-is after Rush auth/registry injection.
|
|
9960
|
+
|
|
9961
|
+
The old "rush-ai skill hand-off" playbook path is deprecated. Use
|
|
9962
|
+
"rush-ai playbook hand-off" instead.
|
|
9847
9963
|
`;
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
const
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9964
|
+
var DISABLE_RESKILL_UPDATE_CHECK_PRELOAD = `data:text/javascript,${encodeURIComponent(`
|
|
9965
|
+
const originalFetch = globalThis.fetch;
|
|
9966
|
+
globalThis.fetch = (input, init) => {
|
|
9967
|
+
const url = typeof input === 'string' ? input : input?.url;
|
|
9968
|
+
if (url === 'https://registry.npmjs.org/reskill') {
|
|
9969
|
+
return Promise.resolve(new Response('', { status: 204 }));
|
|
9970
|
+
}
|
|
9971
|
+
return originalFetch(input, init);
|
|
9972
|
+
};
|
|
9973
|
+
`)}`;
|
|
9974
|
+
var AUTH_LIFECYCLE_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "whoami"]);
|
|
9975
|
+
var RESKILL_JSON_COMMANDS = /* @__PURE__ */ new Set([
|
|
9976
|
+
"find",
|
|
9977
|
+
"search",
|
|
9978
|
+
"list",
|
|
9979
|
+
"ls",
|
|
9980
|
+
"info",
|
|
9981
|
+
"outdated",
|
|
9982
|
+
"doctor"
|
|
9983
|
+
]);
|
|
9984
|
+
var RESKILL_YES_COMMANDS = /* @__PURE__ */ new Set([
|
|
9985
|
+
"install",
|
|
9986
|
+
"i",
|
|
9987
|
+
"uninstall",
|
|
9988
|
+
"un",
|
|
9989
|
+
"remove",
|
|
9990
|
+
"rm",
|
|
9991
|
+
"publish",
|
|
9992
|
+
"pub"
|
|
9993
|
+
]);
|
|
9994
|
+
function hasAnyFlag(args, flags) {
|
|
9995
|
+
return args.some((arg) => flags.includes(arg));
|
|
9996
|
+
}
|
|
9997
|
+
function stripRushOnlyFlags(args) {
|
|
9998
|
+
let ci = false;
|
|
9999
|
+
const forwarded = [];
|
|
10000
|
+
for (const arg of args) {
|
|
10001
|
+
if (arg === "--ci") {
|
|
10002
|
+
ci = true;
|
|
10003
|
+
continue;
|
|
10004
|
+
}
|
|
10005
|
+
if (arg.startsWith("--ci=")) {
|
|
10006
|
+
ci = !["0", "false", "no"].includes(
|
|
10007
|
+
arg.slice("--ci=".length).toLowerCase()
|
|
10008
|
+
);
|
|
10009
|
+
continue;
|
|
10010
|
+
}
|
|
10011
|
+
forwarded.push(arg);
|
|
9860
10012
|
}
|
|
9861
|
-
return
|
|
10013
|
+
return { args: forwarded, ci };
|
|
10014
|
+
}
|
|
10015
|
+
function applyGlobalOptions(args, opts) {
|
|
10016
|
+
const forwarded = [...args];
|
|
10017
|
+
const command = forwarded[0];
|
|
10018
|
+
if (!command) return forwarded;
|
|
10019
|
+
if (opts.json && RESKILL_JSON_COMMANDS.has(command) && !hasAnyFlag(forwarded, ["--json", "-j"])) {
|
|
10020
|
+
forwarded.push("--json");
|
|
10021
|
+
}
|
|
10022
|
+
const needsYes = RESKILL_YES_COMMANDS.has(command) || command === "group" && forwarded[1] === "delete";
|
|
10023
|
+
if (opts.ci && needsYes && !hasAnyFlag(forwarded, ["--yes", "-y"])) {
|
|
10024
|
+
forwarded.push("--yes");
|
|
10025
|
+
}
|
|
10026
|
+
return forwarded;
|
|
10027
|
+
}
|
|
10028
|
+
function createReskillEnv() {
|
|
10029
|
+
const env = { ...process.env };
|
|
10030
|
+
env.RESKILL_REGISTRY = getGlobalConfig().api;
|
|
10031
|
+
const token = getAuthToken();
|
|
10032
|
+
if (token) {
|
|
10033
|
+
env.RESKILL_TOKEN = token;
|
|
10034
|
+
}
|
|
10035
|
+
return env;
|
|
10036
|
+
}
|
|
10037
|
+
function getReskillInvocation() {
|
|
10038
|
+
return {
|
|
10039
|
+
command: process.execPath,
|
|
10040
|
+
args: [
|
|
10041
|
+
"--import",
|
|
10042
|
+
DISABLE_RESKILL_UPDATE_CHECK_PRELOAD,
|
|
10043
|
+
fileURLToPath(import.meta.resolve("reskill/cli"))
|
|
10044
|
+
]
|
|
10045
|
+
};
|
|
10046
|
+
}
|
|
10047
|
+
function runReskill(args) {
|
|
10048
|
+
return new Promise((resolve23, reject) => {
|
|
10049
|
+
const reskill = getReskillInvocation();
|
|
10050
|
+
const child = spawn2(reskill.command, [...reskill.args, ...args], {
|
|
10051
|
+
env: createReskillEnv(),
|
|
10052
|
+
stdio: "inherit"
|
|
10053
|
+
});
|
|
10054
|
+
child.on("error", reject);
|
|
10055
|
+
child.on("close", (code, signal) => {
|
|
10056
|
+
if (signal) {
|
|
10057
|
+
output.error(`reskill exited from signal ${signal}`);
|
|
10058
|
+
resolve23(1);
|
|
10059
|
+
return;
|
|
10060
|
+
}
|
|
10061
|
+
resolve23(code ?? 1);
|
|
10062
|
+
});
|
|
10063
|
+
});
|
|
9862
10064
|
}
|
|
9863
|
-
function
|
|
9864
|
-
|
|
10065
|
+
function maybeHandleLegacyPlaybook(args) {
|
|
10066
|
+
if (args.length === 1 && args[0] === "--list") {
|
|
10067
|
+
output.warn(
|
|
10068
|
+
"`rush-ai skill --list` is deprecated. Use `rush-ai playbook --list` for playbooks or `rush-ai skill list` for installed skills."
|
|
10069
|
+
);
|
|
10070
|
+
printPlaybook(void 0, { list: true });
|
|
10071
|
+
return true;
|
|
10072
|
+
}
|
|
10073
|
+
const [name] = args;
|
|
10074
|
+
if (isPlaybookName(name)) {
|
|
10075
|
+
output.warn(
|
|
10076
|
+
`\`rush-ai skill ${name}\` is deprecated. Use \`rush-ai playbook ${name}\` instead.`
|
|
10077
|
+
);
|
|
10078
|
+
printPlaybook(name);
|
|
10079
|
+
return true;
|
|
10080
|
+
}
|
|
10081
|
+
return false;
|
|
9865
10082
|
}
|
|
9866
10083
|
function registerSkillCommand(program) {
|
|
9867
|
-
program.command("skill [
|
|
9868
|
-
const
|
|
9869
|
-
if (
|
|
9870
|
-
output.error(
|
|
9871
|
-
"Could not locate the skills/ directory. This is a packaging bug \u2014 please file an issue."
|
|
9872
|
-
);
|
|
9873
|
-
process.exit(2);
|
|
10084
|
+
const command = program.command("skill [args...]").description(SKILL_DESCRIPTION).addHelpText("after", SKILL_HELP_AFTER).allowUnknownOption(true).allowExcessArguments(true).action(async (rawArgs) => {
|
|
10085
|
+
const initialArgs = rawArgs ?? [];
|
|
10086
|
+
if (maybeHandleLegacyPlaybook(initialArgs)) {
|
|
9874
10087
|
return;
|
|
9875
10088
|
}
|
|
9876
|
-
const
|
|
9877
|
-
if (
|
|
9878
|
-
|
|
10089
|
+
const stripped = stripRushOnlyFlags(initialArgs);
|
|
10090
|
+
if (stripped.args.length === 0) {
|
|
10091
|
+
command.help();
|
|
9879
10092
|
return;
|
|
9880
10093
|
}
|
|
9881
|
-
const
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
10094
|
+
const [reskillCommand] = stripped.args;
|
|
10095
|
+
if (reskillCommand && AUTH_LIFECYCLE_COMMANDS.has(reskillCommand)) {
|
|
10096
|
+
output.error(
|
|
10097
|
+
`Use \`rush-ai auth ${reskillCommand === "whoami" ? "status" : reskillCommand}\` instead.`
|
|
10098
|
+
);
|
|
10099
|
+
output.dim(
|
|
10100
|
+
"`rush-ai skill` reuses Rush auth automatically; it does not maintain a separate reskill login state."
|
|
10101
|
+
);
|
|
9886
10102
|
process.exit(1);
|
|
9887
10103
|
return;
|
|
9888
10104
|
}
|
|
9889
|
-
|
|
10105
|
+
const globalOpts = command.optsWithGlobals();
|
|
10106
|
+
const forwarded = applyGlobalOptions(stripped.args, {
|
|
10107
|
+
json: globalOpts.json,
|
|
10108
|
+
ci: stripped.ci || isCIMode(globalOpts)
|
|
10109
|
+
});
|
|
10110
|
+
try {
|
|
10111
|
+
const code = await runReskill(forwarded);
|
|
10112
|
+
if (code !== 0) {
|
|
10113
|
+
process.exit(code);
|
|
10114
|
+
}
|
|
10115
|
+
} catch (error) {
|
|
10116
|
+
const err = error;
|
|
10117
|
+
if (err.code === "ENOENT") {
|
|
10118
|
+
output.error(
|
|
10119
|
+
"Could not start the bundled reskill CLI. Reinstall rush-ai and try again."
|
|
10120
|
+
);
|
|
10121
|
+
} else if (err.code === "ERR_PACKAGE_PATH_NOT_EXPORTED" || err.code === "ERR_MODULE_NOT_FOUND") {
|
|
10122
|
+
output.error(
|
|
10123
|
+
"Could not resolve the bundled reskill CLI entry. Reinstall rush-ai and try again."
|
|
10124
|
+
);
|
|
10125
|
+
} else {
|
|
10126
|
+
output.error(`Failed to run reskill: ${err.message}`);
|
|
10127
|
+
}
|
|
10128
|
+
process.exit(1);
|
|
10129
|
+
}
|
|
9890
10130
|
});
|
|
9891
10131
|
}
|
|
9892
10132
|
|
|
@@ -11380,7 +11620,7 @@ function formatCreatedAt(ts) {
|
|
|
11380
11620
|
var MAX_TEXT_LENGTH = 500;
|
|
11381
11621
|
function truncateText(text) {
|
|
11382
11622
|
if (text.length <= MAX_TEXT_LENGTH) return text;
|
|
11383
|
-
return text.slice(0, MAX_TEXT_LENGTH)
|
|
11623
|
+
return `${text.slice(0, MAX_TEXT_LENGTH)}...`;
|
|
11384
11624
|
}
|
|
11385
11625
|
function summarizeTools(parts) {
|
|
11386
11626
|
const toolParts = parts.filter(
|
|
@@ -11411,7 +11651,9 @@ function renderMessages(messages, conversationId, compact) {
|
|
|
11411
11651
|
);
|
|
11412
11652
|
if (textParts.length > 0) {
|
|
11413
11653
|
for (const tp of textParts) {
|
|
11414
|
-
|
|
11654
|
+
if (tp.text) {
|
|
11655
|
+
output.log(` ${truncateText(tp.text)}`);
|
|
11656
|
+
}
|
|
11415
11657
|
}
|
|
11416
11658
|
} else if (msg.content) {
|
|
11417
11659
|
output.log(` ${truncateText(msg.content)}`);
|
|
@@ -11494,7 +11736,7 @@ Typical flows:
|
|
|
11494
11736
|
# Quick one-shot with the default agent (\`rush\`)
|
|
11495
11737
|
$ rush-ai task create -a rush -p "Summarize the latest release notes"
|
|
11496
11738
|
|
|
11497
|
-
For agents: run \`rush-ai
|
|
11739
|
+
For agents: run \`rush-ai playbook hand-off\` for the full playbook.
|
|
11498
11740
|
`;
|
|
11499
11741
|
function registerTaskCommand(program) {
|
|
11500
11742
|
const task = program.command("task").description("Create and manage tasks").addHelpText("after", TASK_HELP_AFTER);
|
|
@@ -11844,7 +12086,7 @@ function registerTaskCommand(program) {
|
|
|
11844
12086
|
}
|
|
11845
12087
|
if (options.last) {
|
|
11846
12088
|
const n = parseInt(options.last, 10);
|
|
11847
|
-
if (!isNaN(n) && n > 0) {
|
|
12089
|
+
if (!Number.isNaN(n) && n > 0) {
|
|
11848
12090
|
messages = messages.slice(-n);
|
|
11849
12091
|
}
|
|
11850
12092
|
}
|
|
@@ -12061,6 +12303,7 @@ function registerCommands(program) {
|
|
|
12061
12303
|
registerAgentCommand(program);
|
|
12062
12304
|
registerTaskCommand(program);
|
|
12063
12305
|
registerSkillCommand(program);
|
|
12306
|
+
registerPlaybookCommand(program);
|
|
12064
12307
|
registerCheckCommand(program);
|
|
12065
12308
|
registerMcpCommand(program);
|
|
12066
12309
|
registerMarketplaceCommand(program);
|
|
@@ -12139,7 +12382,7 @@ var BANNER = `
|
|
|
12139
12382
|
${chalk7.dim("\xB7")} From Cursor / Claude Code, hand off a task to a Rush agent without restating context.
|
|
12140
12383
|
${chalk7.dim("\xB7")} Compose workflows where a Rush specialist agent runs as your sub-agent.
|
|
12141
12384
|
|
|
12142
|
-
${chalk7.dim("For agents:")} ${chalk7.cyan("rush-ai
|
|
12385
|
+
${chalk7.dim("For agents:")} ${chalk7.cyan("rush-ai playbook")} prints ready-to-consume usage playbooks.
|
|
12143
12386
|
`;
|
|
12144
12387
|
function showWelcomeGuide() {
|
|
12145
12388
|
const lines = [
|
|
@@ -12151,11 +12394,12 @@ function showWelcomeGuide() {
|
|
|
12151
12394
|
` ${chalk7.cyan("rush-ai auth login")} Log in to the Rush platform`,
|
|
12152
12395
|
` ${chalk7.cyan("rush-ai agent list")} Browse available agents`,
|
|
12153
12396
|
` ${chalk7.cyan("rush-ai task create -a web-builder")} Hand a task to a Rush agent`,
|
|
12397
|
+
` ${chalk7.cyan("rush-ai skill install <skill>")} Install a Skill through Rush auth`,
|
|
12154
12398
|
"",
|
|
12155
12399
|
chalk7.dim(" For AI agents:"),
|
|
12156
|
-
` ${chalk7.cyan("rush-ai
|
|
12157
|
-
` ${chalk7.cyan("rush-ai
|
|
12158
|
-
` ${chalk7.cyan("rush-ai
|
|
12400
|
+
` ${chalk7.cyan("rush-ai playbook")} Print usage playbooks (markdown)`,
|
|
12401
|
+
` ${chalk7.cyan("rush-ai playbook hand-off")} IDE \u2192 Rush task hand-off`,
|
|
12402
|
+
` ${chalk7.cyan("rush-ai playbook agent-shelf")} Calling a Rush agent as a sub-agent`,
|
|
12159
12403
|
""
|
|
12160
12404
|
];
|
|
12161
12405
|
try {
|