rush-ai 0.15.0 → 0.16.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/AGENTS.md +3 -3
- package/README.md +30 -0
- package/dist/index.js +357 -139
- 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 });
|
|
@@ -4768,8 +4849,8 @@ var ClaudeCodeInstaller = class {
|
|
|
4768
4849
|
const kind = await inodeKind(mktDir);
|
|
4769
4850
|
if (kind === "symlink") {
|
|
4770
4851
|
const target = await readlink(mktDir).catch(() => null);
|
|
4771
|
-
const resolvedTarget = target !== null ?
|
|
4772
|
-
if (resolvedTarget && resolvedTarget ===
|
|
4852
|
+
const resolvedTarget = target !== null ? resolve11(dirname4(mktDir), target) : null;
|
|
4853
|
+
if (resolvedTarget && resolvedTarget === resolve11(src.rootDir)) {
|
|
4773
4854
|
return { written: false, path: mktDir };
|
|
4774
4855
|
}
|
|
4775
4856
|
throw new Error(
|
|
@@ -4838,7 +4919,7 @@ var ClaudeCodeInstaller = class {
|
|
|
4838
4919
|
const pluginVersionDir = this.paths.pluginVersionDir(ref, version);
|
|
4839
4920
|
const dryFiles = [pluginVersionDir];
|
|
4840
4921
|
for (const cap of CAPABILITY_DIRS) {
|
|
4841
|
-
const srcDir =
|
|
4922
|
+
const srcDir = resolve11(plugin.sourceDir, cap);
|
|
4842
4923
|
if (!await dirExists(srcDir)) continue;
|
|
4843
4924
|
const dstDir = this.paths.capabilityDir(ref, version, cap);
|
|
4844
4925
|
dryFiles.push(dstDir);
|
|
@@ -5471,11 +5552,11 @@ import {
|
|
|
5471
5552
|
} from "fs/promises";
|
|
5472
5553
|
import { homedir as homedir6 } from "os";
|
|
5473
5554
|
import {
|
|
5474
|
-
basename as
|
|
5555
|
+
basename as basename3,
|
|
5475
5556
|
dirname as dirname6,
|
|
5476
5557
|
extname,
|
|
5477
5558
|
relative as pathRelative,
|
|
5478
|
-
resolve as
|
|
5559
|
+
resolve as resolve12
|
|
5479
5560
|
} from "path";
|
|
5480
5561
|
|
|
5481
5562
|
// src/installers/codex/mcp.ts
|
|
@@ -6116,10 +6197,10 @@ var CodexInstaller = class {
|
|
|
6116
6197
|
const srcMcp = plugin.manifest.mcpServers;
|
|
6117
6198
|
if (srcMcp !== void 0 && srcMcp !== null) {
|
|
6118
6199
|
if (typeof srcMcp === "string") {
|
|
6119
|
-
const destPath =
|
|
6200
|
+
const destPath = resolve12(versionDir, srcMcp);
|
|
6120
6201
|
files.push(destPath);
|
|
6121
|
-
const srcPath =
|
|
6122
|
-
const rel = pathRelative(
|
|
6202
|
+
const srcPath = resolve12(plugin.sourceDir, srcMcp);
|
|
6203
|
+
const rel = pathRelative(resolve12(plugin.sourceDir), srcPath);
|
|
6123
6204
|
const traversal = rel === ".." || rel.startsWith("..") || rel.startsWith("/");
|
|
6124
6205
|
const exists = await pathExists5(srcPath);
|
|
6125
6206
|
if (traversal || !exists) {
|
|
@@ -6264,13 +6345,13 @@ function assertSafePathComponents(ref, version) {
|
|
|
6264
6345
|
}
|
|
6265
6346
|
async function copyAuthorProvidedMcp(sourceDir, versionDir, relativeRef) {
|
|
6266
6347
|
if (typeof relativeRef !== "string" || relativeRef.length === 0) return null;
|
|
6267
|
-
const srcPath =
|
|
6268
|
-
const rel = pathRelative(
|
|
6348
|
+
const srcPath = resolve12(sourceDir, relativeRef);
|
|
6349
|
+
const rel = pathRelative(resolve12(sourceDir), srcPath);
|
|
6269
6350
|
if (rel === ".." || rel.startsWith("..") || rel.startsWith("/")) {
|
|
6270
6351
|
return null;
|
|
6271
6352
|
}
|
|
6272
6353
|
if (!await pathExists5(srcPath)) return null;
|
|
6273
|
-
const destPath =
|
|
6354
|
+
const destPath = resolve12(versionDir, relativeRef);
|
|
6274
6355
|
await mkdir7(dirname6(destPath), { recursive: true });
|
|
6275
6356
|
const raw = await readFile7(srcPath, "utf8");
|
|
6276
6357
|
await writeFileAtomic(destPath, raw);
|
|
@@ -6287,13 +6368,13 @@ async function copyAuthorProvidedMcp(sourceDir, versionDir, relativeRef) {
|
|
|
6287
6368
|
}
|
|
6288
6369
|
async function writeCodexMarketplaceMirror(input) {
|
|
6289
6370
|
const pluginParent = dirname6(input.pluginDir);
|
|
6290
|
-
const pluginBase =
|
|
6371
|
+
const pluginBase = basename3(input.pluginDir);
|
|
6291
6372
|
await mkdir7(pluginParent, { recursive: true });
|
|
6292
6373
|
const tmpPluginDir = await mkdtemp(
|
|
6293
|
-
|
|
6374
|
+
resolve12(pluginParent, `.${pluginBase}.tmp-`)
|
|
6294
6375
|
);
|
|
6295
6376
|
const backupPluginDir = await mkdtemp(
|
|
6296
|
-
|
|
6377
|
+
resolve12(pluginParent, `.${pluginBase}.bak-`)
|
|
6297
6378
|
);
|
|
6298
6379
|
let hadExistingPluginDir = false;
|
|
6299
6380
|
let swappedPluginDir = false;
|
|
@@ -6326,7 +6407,7 @@ async function writeCodexMarketplaceMirror(input) {
|
|
|
6326
6407
|
try {
|
|
6327
6408
|
if (await pathExists5(input.pluginDir)) {
|
|
6328
6409
|
displacedPluginDir = await mkdtemp(
|
|
6329
|
-
|
|
6410
|
+
resolve12(pluginParent, `.${pluginBase}.failed-`)
|
|
6330
6411
|
);
|
|
6331
6412
|
await rm7(displacedPluginDir, { recursive: true, force: true });
|
|
6332
6413
|
await rename6(input.pluginDir, displacedPluginDir);
|
|
@@ -6413,7 +6494,7 @@ async function planCodexSkills(plugin) {
|
|
|
6413
6494
|
const usedNames = /* @__PURE__ */ new Set();
|
|
6414
6495
|
const coveredAliases = /* @__PURE__ */ new Set();
|
|
6415
6496
|
const commandFiles = await listMarkdownFiles(
|
|
6416
|
-
|
|
6497
|
+
resolve12(plugin.sourceDir, "commands"),
|
|
6417
6498
|
{
|
|
6418
6499
|
recursive: false
|
|
6419
6500
|
}
|
|
@@ -6423,19 +6504,19 @@ async function planCodexSkills(plugin) {
|
|
|
6423
6504
|
commandFiles.map((file2) => markdownSkillAlias(plugin.manifest.name, file2))
|
|
6424
6505
|
)
|
|
6425
6506
|
);
|
|
6426
|
-
const nativeSkillsDir =
|
|
6507
|
+
const nativeSkillsDir = resolve12(plugin.sourceDir, "skills");
|
|
6427
6508
|
if (await hasCodexSkillDir(nativeSkillsDir)) {
|
|
6428
6509
|
for (const name of await listCodexSkillNames(nativeSkillsDir)) {
|
|
6429
6510
|
usedNames.add(name);
|
|
6430
6511
|
coveredAliases.add(skillAlias(plugin.manifest.name, name));
|
|
6431
6512
|
sources.push({
|
|
6432
6513
|
kind: "copy-skill",
|
|
6433
|
-
sourceDir:
|
|
6514
|
+
sourceDir: resolve12(nativeSkillsDir, name),
|
|
6434
6515
|
skillName: name
|
|
6435
6516
|
});
|
|
6436
6517
|
}
|
|
6437
6518
|
} else {
|
|
6438
|
-
const dotSkillsDir =
|
|
6519
|
+
const dotSkillsDir = resolve12(plugin.sourceDir, ".skills");
|
|
6439
6520
|
const claudeSkills = await listMarkdownFiles(dotSkillsDir);
|
|
6440
6521
|
const claudeAliases = /* @__PURE__ */ new Map();
|
|
6441
6522
|
for (const file2 of claudeSkills) {
|
|
@@ -6474,7 +6555,7 @@ async function planCodexSkills(plugin) {
|
|
|
6474
6555
|
async function materializeCodexSkills(plan, dstSkills) {
|
|
6475
6556
|
for (const source of plan.sources) {
|
|
6476
6557
|
if (source.kind === "copy-skill") {
|
|
6477
|
-
await cp(source.sourceDir,
|
|
6558
|
+
await cp(source.sourceDir, resolve12(dstSkills, source.skillName), {
|
|
6478
6559
|
recursive: true,
|
|
6479
6560
|
dereference: false,
|
|
6480
6561
|
preserveTimestamps: true,
|
|
@@ -6485,7 +6566,7 @@ async function materializeCodexSkills(plan, dstSkills) {
|
|
|
6485
6566
|
const raw = await readFile7(source.sourceFile, "utf8");
|
|
6486
6567
|
const normalized = normalizeSkillMarkdown(raw, source.skillName);
|
|
6487
6568
|
await writeFileAtomic(
|
|
6488
|
-
|
|
6569
|
+
resolve12(dstSkills, source.skillName, "SKILL.md"),
|
|
6489
6570
|
normalized
|
|
6490
6571
|
);
|
|
6491
6572
|
}
|
|
@@ -6571,7 +6652,7 @@ async function listCodexSkillNames(skillsDir) {
|
|
|
6571
6652
|
const names = [];
|
|
6572
6653
|
for (const entry of entries) {
|
|
6573
6654
|
if (!entry.isDirectory()) continue;
|
|
6574
|
-
if (await pathExists5(
|
|
6655
|
+
if (await pathExists5(resolve12(skillsDir, entry.name, "SKILL.md"))) {
|
|
6575
6656
|
names.push(entry.name);
|
|
6576
6657
|
}
|
|
6577
6658
|
}
|
|
@@ -6589,7 +6670,7 @@ async function listMarkdownFiles(dir, opts = {}) {
|
|
|
6589
6670
|
return;
|
|
6590
6671
|
}
|
|
6591
6672
|
for (const entry of entries) {
|
|
6592
|
-
const abs =
|
|
6673
|
+
const abs = resolve12(current, entry.name);
|
|
6593
6674
|
if (entry.isDirectory()) {
|
|
6594
6675
|
if (recursive) await walk2(abs);
|
|
6595
6676
|
continue;
|
|
@@ -6617,7 +6698,7 @@ async function markdownSkillAlias(pluginName, filePath) {
|
|
|
6617
6698
|
const triggerAlias = extractTriggerAlias(await readFile7(filePath, "utf8"));
|
|
6618
6699
|
return skillAlias(
|
|
6619
6700
|
pluginName,
|
|
6620
|
-
triggerAlias ??
|
|
6701
|
+
triggerAlias ?? basename3(filePath, extname(filePath))
|
|
6621
6702
|
);
|
|
6622
6703
|
}
|
|
6623
6704
|
function markdownPathAlias(pluginName, baseDir, filePath) {
|
|
@@ -6681,7 +6762,7 @@ function parsePluginKey2(key) {
|
|
|
6681
6762
|
return { name: key.slice(0, at), marketplace: key.slice(at + 1) };
|
|
6682
6763
|
}
|
|
6683
6764
|
async function detectInstalledVersion(home, ref) {
|
|
6684
|
-
const baseDir =
|
|
6765
|
+
const baseDir = resolve12(
|
|
6685
6766
|
codexHomeDir(home),
|
|
6686
6767
|
"plugins",
|
|
6687
6768
|
"cache",
|
|
@@ -6697,8 +6778,8 @@ async function detectInstalledVersion(home, ref) {
|
|
|
6697
6778
|
}
|
|
6698
6779
|
let best = null;
|
|
6699
6780
|
for (const entry of entries) {
|
|
6700
|
-
const versionDir =
|
|
6701
|
-
const manifestPath =
|
|
6781
|
+
const versionDir = resolve12(baseDir, entry);
|
|
6782
|
+
const manifestPath = resolve12(versionDir, ".codex-plugin", "plugin.json");
|
|
6702
6783
|
if (!await pathExists5(manifestPath)) continue;
|
|
6703
6784
|
try {
|
|
6704
6785
|
const raw = await readFile7(manifestPath, "utf8");
|
|
@@ -6739,7 +6820,7 @@ import {
|
|
|
6739
6820
|
writeFile as writeFile8
|
|
6740
6821
|
} from "fs/promises";
|
|
6741
6822
|
import { homedir as homedir7 } from "os";
|
|
6742
|
-
import { dirname as dirname8, relative, resolve as
|
|
6823
|
+
import { dirname as dirname8, relative, resolve as resolve15 } from "path";
|
|
6743
6824
|
|
|
6744
6825
|
// src/installers/cursor/marker.ts
|
|
6745
6826
|
var RUSH_AI_MARKER = "<!-- rush-ai:auto-generated -->";
|
|
@@ -6858,25 +6939,25 @@ async function pathExists6(p) {
|
|
|
6858
6939
|
}
|
|
6859
6940
|
|
|
6860
6941
|
// src/installers/cursor/paths.ts
|
|
6861
|
-
import { resolve as
|
|
6942
|
+
import { resolve as resolve13 } from "path";
|
|
6862
6943
|
var CURSOR_RELATIVE_DIR = ".cursor";
|
|
6863
6944
|
function cursorDir(home) {
|
|
6864
|
-
return
|
|
6945
|
+
return resolve13(home, CURSOR_RELATIVE_DIR);
|
|
6865
6946
|
}
|
|
6866
6947
|
function cursorMcpJsonPath(home) {
|
|
6867
|
-
return
|
|
6948
|
+
return resolve13(cursorDir(home), "mcp.json");
|
|
6868
6949
|
}
|
|
6869
6950
|
function cursorRulesDir(home) {
|
|
6870
|
-
return
|
|
6951
|
+
return resolve13(cursorDir(home), "rules");
|
|
6871
6952
|
}
|
|
6872
6953
|
function cursorRuleMdcPath(home, ruleName) {
|
|
6873
|
-
return
|
|
6954
|
+
return resolve13(cursorRulesDir(home), `${ruleName}.mdc`);
|
|
6874
6955
|
}
|
|
6875
6956
|
function cursorSkillsDir(home) {
|
|
6876
|
-
return
|
|
6957
|
+
return resolve13(cursorDir(home), "skills");
|
|
6877
6958
|
}
|
|
6878
6959
|
function cursorSkillDir(home, skillName) {
|
|
6879
|
-
return
|
|
6960
|
+
return resolve13(cursorSkillsDir(home), skillName);
|
|
6880
6961
|
}
|
|
6881
6962
|
function bridgeSkillFileReference(skillName) {
|
|
6882
6963
|
return `.cursor/skills/${skillName}/SKILL.md`;
|
|
@@ -6987,9 +7068,9 @@ function quoteIfNeeded(value) {
|
|
|
6987
7068
|
|
|
6988
7069
|
// src/installers/cursor/skills.ts
|
|
6989
7070
|
import { readFile as readFile9 } from "fs/promises";
|
|
6990
|
-
import { resolve as
|
|
7071
|
+
import { resolve as resolve14 } from "path";
|
|
6991
7072
|
async function readSkillDescription(skillSourceDir) {
|
|
6992
|
-
const skillMdPath =
|
|
7073
|
+
const skillMdPath = resolve14(skillSourceDir, "SKILL.md");
|
|
6993
7074
|
let raw;
|
|
6994
7075
|
try {
|
|
6995
7076
|
raw = await readFile9(skillMdPath, "utf8");
|
|
@@ -7352,10 +7433,10 @@ var CursorInstaller = class {
|
|
|
7352
7433
|
const prevEntry = registryStore.get(plugin.ref);
|
|
7353
7434
|
const prevFiles = new Set(prevEntry?.targets?.cursor?.files ?? []);
|
|
7354
7435
|
if (plugin.capabilities.includes("skills")) {
|
|
7355
|
-
const skillsSourceDir =
|
|
7436
|
+
const skillsSourceDir = resolve15(plugin.sourceDir, "skills");
|
|
7356
7437
|
const skillDirs = await listSkillDirs(skillsSourceDir);
|
|
7357
7438
|
for (const skillName of skillDirs) {
|
|
7358
|
-
const srcDir =
|
|
7439
|
+
const srcDir = resolve15(skillsSourceDir, skillName);
|
|
7359
7440
|
const destDir = cursorSkillDir(this.home, skillName);
|
|
7360
7441
|
const bridgeRulePath = cursorRuleMdcPath(this.home, skillName);
|
|
7361
7442
|
if (await dirExists2(destDir)) {
|
|
@@ -7387,10 +7468,10 @@ var CursorInstaller = class {
|
|
|
7387
7468
|
}
|
|
7388
7469
|
}
|
|
7389
7470
|
if (plugin.capabilities.includes("rules")) {
|
|
7390
|
-
const rulesSourceDir =
|
|
7471
|
+
const rulesSourceDir = resolve15(plugin.sourceDir, "rules");
|
|
7391
7472
|
const rulesFiles = await listRuleMdFiles(rulesSourceDir);
|
|
7392
7473
|
for (const ruleFile of rulesFiles) {
|
|
7393
|
-
const srcPath =
|
|
7474
|
+
const srcPath = resolve15(rulesSourceDir, ruleFile);
|
|
7394
7475
|
const ruleName = ruleFile.replace(/\.md$/i, "");
|
|
7395
7476
|
const destPath = cursorRuleMdcPath(this.home, ruleName);
|
|
7396
7477
|
await assertMdcWritable(destPath, args.force);
|
|
@@ -7613,12 +7694,12 @@ async function classifyArtifactPath(p) {
|
|
|
7613
7694
|
// src/migration/cleanup.ts
|
|
7614
7695
|
import { lstat as lstat3, rm as rm10, unlink as unlink2 } from "fs/promises";
|
|
7615
7696
|
import { homedir as homedir9 } from "os";
|
|
7616
|
-
import { resolve as
|
|
7697
|
+
import { resolve as resolve17 } from "path";
|
|
7617
7698
|
|
|
7618
7699
|
// src/migration/detect.ts
|
|
7619
7700
|
import { access as access9, lstat as lstat2, readFile as readFile11, readlink as readlink2, stat as stat8 } from "fs/promises";
|
|
7620
7701
|
import { homedir as homedir8 } from "os";
|
|
7621
|
-
import { isAbsolute as isAbsolute4, resolve as
|
|
7702
|
+
import { isAbsolute as isAbsolute4, resolve as resolve16 } from "path";
|
|
7622
7703
|
var LEGACY_ASSET_MANIFEST_RELATIVE = ".rush/plugins/claude-code/asset-manifest.json";
|
|
7623
7704
|
var LEGACY_ASSETS_DIR_RELATIVE = ".rush/plugins/claude-code/assets";
|
|
7624
7705
|
var LEGACY_CLAUDE_CODE_DIR_RELATIVE = ".rush/plugins/claude-code";
|
|
@@ -7637,7 +7718,7 @@ async function isSymlinkPointingInto(linkPath, expectedTarget) {
|
|
|
7637
7718
|
const lst = await lstat2(linkPath);
|
|
7638
7719
|
if (!lst.isSymbolicLink()) return false;
|
|
7639
7720
|
const rawTarget = await readlink2(linkPath);
|
|
7640
|
-
const resolvedTarget = isAbsolute4(rawTarget) ? rawTarget :
|
|
7721
|
+
const resolvedTarget = isAbsolute4(rawTarget) ? rawTarget : resolve16(linkPath, "..", rawTarget);
|
|
7641
7722
|
return resolvedTarget === expectedTarget || resolvedTarget.startsWith(`${expectedTarget}/`);
|
|
7642
7723
|
} catch {
|
|
7643
7724
|
return false;
|
|
@@ -7674,10 +7755,10 @@ async function hasLegacyMcpWithoutEnabled(settingsJsonPath) {
|
|
|
7674
7755
|
}
|
|
7675
7756
|
async function detectLegacyInstall(opts = {}) {
|
|
7676
7757
|
const home = opts.home ?? homedir8();
|
|
7677
|
-
const assetManifestPath =
|
|
7678
|
-
const legacyAssetsDir =
|
|
7679
|
-
const skillSymlinkPath =
|
|
7680
|
-
const settingsJsonPath =
|
|
7758
|
+
const assetManifestPath = resolve16(home, LEGACY_ASSET_MANIFEST_RELATIVE);
|
|
7759
|
+
const legacyAssetsDir = resolve16(home, LEGACY_ASSETS_DIR_RELATIVE);
|
|
7760
|
+
const skillSymlinkPath = resolve16(home, LEGACY_SKILL_SYMLINK_RELATIVE);
|
|
7761
|
+
const settingsJsonPath = resolve16(home, CLAUDE_SETTINGS_JSON_RELATIVE);
|
|
7681
7762
|
const [hasAssetManifest, hasLegacySkillSymlink, legacyMcpWithoutEnabled] = await Promise.all([
|
|
7682
7763
|
pathExists7(assetManifestPath),
|
|
7683
7764
|
isSymlinkPointingInto(skillSymlinkPath, legacyAssetsDir),
|
|
@@ -7692,7 +7773,7 @@ async function detectLegacyInstall(opts = {}) {
|
|
|
7692
7773
|
}
|
|
7693
7774
|
async function detectLegacyVersion(opts = {}) {
|
|
7694
7775
|
const home = opts.home ?? homedir8();
|
|
7695
|
-
const manifestPath =
|
|
7776
|
+
const manifestPath = resolve16(home, LEGACY_ASSET_MANIFEST_RELATIVE);
|
|
7696
7777
|
try {
|
|
7697
7778
|
const lst = await stat8(manifestPath);
|
|
7698
7779
|
if (!lst.isFile()) return "0.7.x";
|
|
@@ -7710,7 +7791,7 @@ async function detectLegacyVersion(opts = {}) {
|
|
|
7710
7791
|
|
|
7711
7792
|
// src/migration/cleanup.ts
|
|
7712
7793
|
async function cleanupLegacyDir(home) {
|
|
7713
|
-
const dir =
|
|
7794
|
+
const dir = resolve17(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
|
|
7714
7795
|
try {
|
|
7715
7796
|
await rm10(dir, { recursive: true, force: true });
|
|
7716
7797
|
return { ok: true };
|
|
@@ -7722,7 +7803,7 @@ async function cleanupLegacyDir(home) {
|
|
|
7722
7803
|
}
|
|
7723
7804
|
}
|
|
7724
7805
|
async function cleanupLegacySymlink(home) {
|
|
7725
|
-
const path4 =
|
|
7806
|
+
const path4 = resolve17(home, LEGACY_SKILL_SYMLINK_RELATIVE);
|
|
7726
7807
|
try {
|
|
7727
7808
|
let lst;
|
|
7728
7809
|
try {
|
|
@@ -7744,7 +7825,7 @@ async function cleanupLegacySymlink(home) {
|
|
|
7744
7825
|
}
|
|
7745
7826
|
}
|
|
7746
7827
|
async function cleanupLegacyMcp(home) {
|
|
7747
|
-
const path4 =
|
|
7828
|
+
const path4 = resolve17(home, CLAUDE_SETTINGS_JSON_RELATIVE);
|
|
7748
7829
|
try {
|
|
7749
7830
|
const { data, existed } = await readJsonFile(
|
|
7750
7831
|
path4,
|
|
@@ -7802,11 +7883,11 @@ function errorMessage(err) {
|
|
|
7802
7883
|
// src/migration/log.ts
|
|
7803
7884
|
import { appendFile as appendFile2, mkdir as mkdir10 } from "fs/promises";
|
|
7804
7885
|
import { homedir as homedir10 } from "os";
|
|
7805
|
-
import { dirname as dirname9, resolve as
|
|
7886
|
+
import { dirname as dirname9, resolve as resolve18 } from "path";
|
|
7806
7887
|
var MIGRATION_LOG_RELATIVE_PATH = ".rush/plugins/migration-failed.log";
|
|
7807
7888
|
function resolveMigrationLogPath(opts = {}) {
|
|
7808
7889
|
const home = opts.home ?? homedir10();
|
|
7809
|
-
return
|
|
7890
|
+
return resolve18(home, MIGRATION_LOG_RELATIVE_PATH);
|
|
7810
7891
|
}
|
|
7811
7892
|
async function appendMigrationFailure(reason, opts = {}) {
|
|
7812
7893
|
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -7847,7 +7928,7 @@ import {
|
|
|
7847
7928
|
// src/plugins/capabilities.ts
|
|
7848
7929
|
import { constants as fsConstants9 } from "fs";
|
|
7849
7930
|
import { access as access10, readdir as readdir4, stat as stat9 } from "fs/promises";
|
|
7850
|
-
import { extname as extname2, resolve as
|
|
7931
|
+
import { extname as extname2, resolve as resolve19 } from "path";
|
|
7851
7932
|
var CAPABILITY_ORDER = [
|
|
7852
7933
|
"commands",
|
|
7853
7934
|
"skills",
|
|
@@ -7876,10 +7957,10 @@ async function scanCapabilities(sourceDir, manifest) {
|
|
|
7876
7957
|
return CAPABILITY_ORDER.filter((cap) => found.has(cap));
|
|
7877
7958
|
}
|
|
7878
7959
|
async function hasCommands(sourceDir) {
|
|
7879
|
-
return dirHasFileWithExt(
|
|
7960
|
+
return dirHasFileWithExt(resolve19(sourceDir, "commands"), ".md");
|
|
7880
7961
|
}
|
|
7881
7962
|
async function hasSkills(sourceDir) {
|
|
7882
|
-
const skillsDir =
|
|
7963
|
+
const skillsDir = resolve19(sourceDir, "skills");
|
|
7883
7964
|
if (await dirExists3(skillsDir)) {
|
|
7884
7965
|
let entries;
|
|
7885
7966
|
try {
|
|
@@ -7889,7 +7970,7 @@ async function hasSkills(sourceDir) {
|
|
|
7889
7970
|
}
|
|
7890
7971
|
for (const entry of entries) {
|
|
7891
7972
|
if (!entry.isDirectory()) continue;
|
|
7892
|
-
const skillMdPath =
|
|
7973
|
+
const skillMdPath = resolve19(skillsDir, entry.name, "SKILL.md");
|
|
7893
7974
|
if (await fileExists2(skillMdPath)) {
|
|
7894
7975
|
return true;
|
|
7895
7976
|
}
|
|
@@ -7898,10 +7979,10 @@ async function hasSkills(sourceDir) {
|
|
|
7898
7979
|
return hasClaudeStyleSkills(sourceDir);
|
|
7899
7980
|
}
|
|
7900
7981
|
async function hasRules(sourceDir) {
|
|
7901
|
-
return dirHasFileWithExt(
|
|
7982
|
+
return dirHasFileWithExt(resolve19(sourceDir, "rules"), ".md");
|
|
7902
7983
|
}
|
|
7903
7984
|
async function hasHooks(sourceDir) {
|
|
7904
|
-
const hooksDir =
|
|
7985
|
+
const hooksDir = resolve19(sourceDir, "hooks");
|
|
7905
7986
|
if (!await dirExists3(hooksDir)) {
|
|
7906
7987
|
return false;
|
|
7907
7988
|
}
|
|
@@ -7943,7 +8024,7 @@ async function dirHasFileWithExt(dirPath, ext) {
|
|
|
7943
8024
|
);
|
|
7944
8025
|
}
|
|
7945
8026
|
async function hasClaudeStyleSkills(sourceDir) {
|
|
7946
|
-
const dotSkillsDir =
|
|
8027
|
+
const dotSkillsDir = resolve19(sourceDir, ".skills");
|
|
7947
8028
|
if (!await dirExists3(dotSkillsDir)) {
|
|
7948
8029
|
return false;
|
|
7949
8030
|
}
|
|
@@ -7955,7 +8036,7 @@ async function hasClaudeStyleSkills(sourceDir) {
|
|
|
7955
8036
|
return false;
|
|
7956
8037
|
}
|
|
7957
8038
|
for (const entry of entries) {
|
|
7958
|
-
const abs =
|
|
8039
|
+
const abs = resolve19(dirPath, entry.name);
|
|
7959
8040
|
if (entry.isDirectory()) {
|
|
7960
8041
|
if (await walk2(abs)) return true;
|
|
7961
8042
|
continue;
|
|
@@ -8050,10 +8131,10 @@ var PluginCloneFailedError = class extends PluginResolverError {
|
|
|
8050
8131
|
// src/plugins/manifest.ts
|
|
8051
8132
|
import { constants as fsConstants10 } from "fs";
|
|
8052
8133
|
import { access as access11, readFile as readFile12 } from "fs/promises";
|
|
8053
|
-
import { resolve as
|
|
8134
|
+
import { resolve as resolve20 } from "path";
|
|
8054
8135
|
var PLUGIN_MANIFEST_RELATIVE_PATH = ".claude-plugin/plugin.json";
|
|
8055
8136
|
async function readPluginManifest(pluginName, sourceDir) {
|
|
8056
|
-
const manifestPath =
|
|
8137
|
+
const manifestPath = resolve20(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
|
|
8057
8138
|
try {
|
|
8058
8139
|
await access11(manifestPath, fsConstants10.R_OK);
|
|
8059
8140
|
} catch {
|
|
@@ -8951,8 +9032,8 @@ async function runInstall(input) {
|
|
|
8951
9032
|
}
|
|
8952
9033
|
}
|
|
8953
9034
|
if (marketplace.source.kind === "rush") {
|
|
8954
|
-
const pluginDir =
|
|
8955
|
-
const manifestPath =
|
|
9035
|
+
const pluginDir = resolve21(marketplace.rootDir, "plugins", ref.name);
|
|
9036
|
+
const manifestPath = resolve21(pluginDir, ".claude-plugin/plugin.json");
|
|
8956
9037
|
const alreadyMaterialized = await pathExists2(manifestPath);
|
|
8957
9038
|
const hasNewSecrets = input.secrets && Object.keys(input.secrets).length > 0;
|
|
8958
9039
|
const shouldMaterialize = !alreadyMaterialized || force || hasNewSecrets;
|
|
@@ -9831,62 +9912,195 @@ function registerPluginCommand(program) {
|
|
|
9831
9912
|
}
|
|
9832
9913
|
|
|
9833
9914
|
// src/commands/skill/index.ts
|
|
9834
|
-
import {
|
|
9835
|
-
import {
|
|
9836
|
-
var SKILL_DESCRIPTION = "
|
|
9915
|
+
import { spawn as spawn2 } from "child_process";
|
|
9916
|
+
import { fileURLToPath } from "url";
|
|
9917
|
+
var SKILL_DESCRIPTION = "Manage AI agent skills through reskill using Rush auth and registry defaults.";
|
|
9837
9918
|
var SKILL_HELP_AFTER = `
|
|
9838
9919
|
Examples:
|
|
9839
|
-
$
|
|
9840
|
-
$
|
|
9841
|
-
$
|
|
9842
|
-
$
|
|
9920
|
+
$ rush-ai skill install @kanyun/rush-find-skills
|
|
9921
|
+
$ rush-ai skill find vue
|
|
9922
|
+
$ rush-ai skill list
|
|
9923
|
+
$ rush-ai skill publish --dry-run
|
|
9924
|
+
$ rush-ai skill group list
|
|
9843
9925
|
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9926
|
+
Auth:
|
|
9927
|
+
Use rush-ai auth login/logout/status. rush-ai skill forwards your Rush
|
|
9928
|
+
token to the bundled reskill package manager via RESKILL_TOKEN and
|
|
9929
|
+
defaults RESKILL_REGISTRY to the active Rush API URL.
|
|
9930
|
+
|
|
9931
|
+
Notes:
|
|
9932
|
+
rush-ai skill is a thin facade: unknown reskill flags and arguments are
|
|
9933
|
+
forwarded as-is after Rush auth/registry injection.
|
|
9934
|
+
|
|
9935
|
+
The old "rush-ai skill hand-off" playbook path is deprecated. Use
|
|
9936
|
+
"rush-ai playbook hand-off" instead.
|
|
9847
9937
|
`;
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
const
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9938
|
+
var DISABLE_RESKILL_UPDATE_CHECK_PRELOAD = `data:text/javascript,${encodeURIComponent(`
|
|
9939
|
+
const originalFetch = globalThis.fetch;
|
|
9940
|
+
globalThis.fetch = (input, init) => {
|
|
9941
|
+
const url = typeof input === 'string' ? input : input?.url;
|
|
9942
|
+
if (url === 'https://registry.npmjs.org/reskill') {
|
|
9943
|
+
return Promise.resolve(new Response('', { status: 204 }));
|
|
9944
|
+
}
|
|
9945
|
+
return originalFetch(input, init);
|
|
9946
|
+
};
|
|
9947
|
+
`)}`;
|
|
9948
|
+
var AUTH_LIFECYCLE_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "whoami"]);
|
|
9949
|
+
var RESKILL_JSON_COMMANDS = /* @__PURE__ */ new Set([
|
|
9950
|
+
"find",
|
|
9951
|
+
"search",
|
|
9952
|
+
"list",
|
|
9953
|
+
"ls",
|
|
9954
|
+
"info",
|
|
9955
|
+
"outdated",
|
|
9956
|
+
"doctor"
|
|
9957
|
+
]);
|
|
9958
|
+
var RESKILL_YES_COMMANDS = /* @__PURE__ */ new Set([
|
|
9959
|
+
"install",
|
|
9960
|
+
"i",
|
|
9961
|
+
"uninstall",
|
|
9962
|
+
"un",
|
|
9963
|
+
"remove",
|
|
9964
|
+
"rm",
|
|
9965
|
+
"publish",
|
|
9966
|
+
"pub"
|
|
9967
|
+
]);
|
|
9968
|
+
function hasAnyFlag(args, flags) {
|
|
9969
|
+
return args.some((arg) => flags.includes(arg));
|
|
9970
|
+
}
|
|
9971
|
+
function stripRushOnlyFlags(args) {
|
|
9972
|
+
let ci = false;
|
|
9973
|
+
const forwarded = [];
|
|
9974
|
+
for (const arg of args) {
|
|
9975
|
+
if (arg === "--ci") {
|
|
9976
|
+
ci = true;
|
|
9977
|
+
continue;
|
|
9978
|
+
}
|
|
9979
|
+
if (arg.startsWith("--ci=")) {
|
|
9980
|
+
ci = !["0", "false", "no"].includes(
|
|
9981
|
+
arg.slice("--ci=".length).toLowerCase()
|
|
9982
|
+
);
|
|
9983
|
+
continue;
|
|
9984
|
+
}
|
|
9985
|
+
forwarded.push(arg);
|
|
9860
9986
|
}
|
|
9861
|
-
return
|
|
9987
|
+
return { args: forwarded, ci };
|
|
9988
|
+
}
|
|
9989
|
+
function applyGlobalOptions(args, opts) {
|
|
9990
|
+
const forwarded = [...args];
|
|
9991
|
+
const command = forwarded[0];
|
|
9992
|
+
if (!command) return forwarded;
|
|
9993
|
+
if (opts.json && RESKILL_JSON_COMMANDS.has(command) && !hasAnyFlag(forwarded, ["--json", "-j"])) {
|
|
9994
|
+
forwarded.push("--json");
|
|
9995
|
+
}
|
|
9996
|
+
const needsYes = RESKILL_YES_COMMANDS.has(command) || command === "group" && forwarded[1] === "delete";
|
|
9997
|
+
if (opts.ci && needsYes && !hasAnyFlag(forwarded, ["--yes", "-y"])) {
|
|
9998
|
+
forwarded.push("--yes");
|
|
9999
|
+
}
|
|
10000
|
+
return forwarded;
|
|
10001
|
+
}
|
|
10002
|
+
function createReskillEnv() {
|
|
10003
|
+
const env = { ...process.env };
|
|
10004
|
+
env.RESKILL_REGISTRY = getGlobalConfig().api;
|
|
10005
|
+
const token = getAuthToken();
|
|
10006
|
+
if (token) {
|
|
10007
|
+
env.RESKILL_TOKEN = token;
|
|
10008
|
+
}
|
|
10009
|
+
return env;
|
|
10010
|
+
}
|
|
10011
|
+
function getReskillInvocation() {
|
|
10012
|
+
return {
|
|
10013
|
+
command: process.execPath,
|
|
10014
|
+
args: [
|
|
10015
|
+
"--import",
|
|
10016
|
+
DISABLE_RESKILL_UPDATE_CHECK_PRELOAD,
|
|
10017
|
+
fileURLToPath(import.meta.resolve("reskill/cli"))
|
|
10018
|
+
]
|
|
10019
|
+
};
|
|
9862
10020
|
}
|
|
9863
|
-
function
|
|
9864
|
-
return
|
|
10021
|
+
function runReskill(args) {
|
|
10022
|
+
return new Promise((resolve23, reject) => {
|
|
10023
|
+
const reskill = getReskillInvocation();
|
|
10024
|
+
const child = spawn2(reskill.command, [...reskill.args, ...args], {
|
|
10025
|
+
env: createReskillEnv(),
|
|
10026
|
+
stdio: "inherit"
|
|
10027
|
+
});
|
|
10028
|
+
child.on("error", reject);
|
|
10029
|
+
child.on("close", (code, signal) => {
|
|
10030
|
+
if (signal) {
|
|
10031
|
+
output.error(`reskill exited from signal ${signal}`);
|
|
10032
|
+
resolve23(1);
|
|
10033
|
+
return;
|
|
10034
|
+
}
|
|
10035
|
+
resolve23(code ?? 1);
|
|
10036
|
+
});
|
|
10037
|
+
});
|
|
10038
|
+
}
|
|
10039
|
+
function maybeHandleLegacyPlaybook(args) {
|
|
10040
|
+
if (args.length === 1 && args[0] === "--list") {
|
|
10041
|
+
output.warn(
|
|
10042
|
+
"`rush-ai skill --list` is deprecated. Use `rush-ai playbook --list` for playbooks or `rush-ai skill list` for installed skills."
|
|
10043
|
+
);
|
|
10044
|
+
printPlaybook(void 0, { list: true });
|
|
10045
|
+
return true;
|
|
10046
|
+
}
|
|
10047
|
+
const [name] = args;
|
|
10048
|
+
if (isPlaybookName(name)) {
|
|
10049
|
+
output.warn(
|
|
10050
|
+
`\`rush-ai skill ${name}\` is deprecated. Use \`rush-ai playbook ${name}\` instead.`
|
|
10051
|
+
);
|
|
10052
|
+
printPlaybook(name);
|
|
10053
|
+
return true;
|
|
10054
|
+
}
|
|
10055
|
+
return false;
|
|
9865
10056
|
}
|
|
9866
10057
|
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);
|
|
10058
|
+
const command = program.command("skill [args...]").description(SKILL_DESCRIPTION).addHelpText("after", SKILL_HELP_AFTER).allowUnknownOption(true).allowExcessArguments(true).action(async (rawArgs) => {
|
|
10059
|
+
const initialArgs = rawArgs ?? [];
|
|
10060
|
+
if (maybeHandleLegacyPlaybook(initialArgs)) {
|
|
9874
10061
|
return;
|
|
9875
10062
|
}
|
|
9876
|
-
const
|
|
9877
|
-
if (
|
|
9878
|
-
|
|
10063
|
+
const stripped = stripRushOnlyFlags(initialArgs);
|
|
10064
|
+
if (stripped.args.length === 0) {
|
|
10065
|
+
command.help();
|
|
9879
10066
|
return;
|
|
9880
10067
|
}
|
|
9881
|
-
const
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
10068
|
+
const [reskillCommand] = stripped.args;
|
|
10069
|
+
if (reskillCommand && AUTH_LIFECYCLE_COMMANDS.has(reskillCommand)) {
|
|
10070
|
+
output.error(
|
|
10071
|
+
`Use \`rush-ai auth ${reskillCommand === "whoami" ? "status" : reskillCommand}\` instead.`
|
|
10072
|
+
);
|
|
10073
|
+
output.dim(
|
|
10074
|
+
"`rush-ai skill` reuses Rush auth automatically; it does not maintain a separate reskill login state."
|
|
10075
|
+
);
|
|
9886
10076
|
process.exit(1);
|
|
9887
10077
|
return;
|
|
9888
10078
|
}
|
|
9889
|
-
|
|
10079
|
+
const globalOpts = command.optsWithGlobals();
|
|
10080
|
+
const forwarded = applyGlobalOptions(stripped.args, {
|
|
10081
|
+
json: globalOpts.json,
|
|
10082
|
+
ci: stripped.ci || isCIMode(globalOpts)
|
|
10083
|
+
});
|
|
10084
|
+
try {
|
|
10085
|
+
const code = await runReskill(forwarded);
|
|
10086
|
+
if (code !== 0) {
|
|
10087
|
+
process.exit(code);
|
|
10088
|
+
}
|
|
10089
|
+
} catch (error) {
|
|
10090
|
+
const err = error;
|
|
10091
|
+
if (err.code === "ENOENT") {
|
|
10092
|
+
output.error(
|
|
10093
|
+
"Could not start the bundled reskill CLI. Reinstall rush-ai and try again."
|
|
10094
|
+
);
|
|
10095
|
+
} else if (err.code === "ERR_PACKAGE_PATH_NOT_EXPORTED" || err.code === "ERR_MODULE_NOT_FOUND") {
|
|
10096
|
+
output.error(
|
|
10097
|
+
"Could not resolve the bundled reskill CLI entry. Reinstall rush-ai and try again."
|
|
10098
|
+
);
|
|
10099
|
+
} else {
|
|
10100
|
+
output.error(`Failed to run reskill: ${err.message}`);
|
|
10101
|
+
}
|
|
10102
|
+
process.exit(1);
|
|
10103
|
+
}
|
|
9890
10104
|
});
|
|
9891
10105
|
}
|
|
9892
10106
|
|
|
@@ -11380,7 +11594,7 @@ function formatCreatedAt(ts) {
|
|
|
11380
11594
|
var MAX_TEXT_LENGTH = 500;
|
|
11381
11595
|
function truncateText(text) {
|
|
11382
11596
|
if (text.length <= MAX_TEXT_LENGTH) return text;
|
|
11383
|
-
return text.slice(0, MAX_TEXT_LENGTH)
|
|
11597
|
+
return `${text.slice(0, MAX_TEXT_LENGTH)}...`;
|
|
11384
11598
|
}
|
|
11385
11599
|
function summarizeTools(parts) {
|
|
11386
11600
|
const toolParts = parts.filter(
|
|
@@ -11411,7 +11625,9 @@ function renderMessages(messages, conversationId, compact) {
|
|
|
11411
11625
|
);
|
|
11412
11626
|
if (textParts.length > 0) {
|
|
11413
11627
|
for (const tp of textParts) {
|
|
11414
|
-
|
|
11628
|
+
if (tp.text) {
|
|
11629
|
+
output.log(` ${truncateText(tp.text)}`);
|
|
11630
|
+
}
|
|
11415
11631
|
}
|
|
11416
11632
|
} else if (msg.content) {
|
|
11417
11633
|
output.log(` ${truncateText(msg.content)}`);
|
|
@@ -11494,7 +11710,7 @@ Typical flows:
|
|
|
11494
11710
|
# Quick one-shot with the default agent (\`rush\`)
|
|
11495
11711
|
$ rush-ai task create -a rush -p "Summarize the latest release notes"
|
|
11496
11712
|
|
|
11497
|
-
For agents: run \`rush-ai
|
|
11713
|
+
For agents: run \`rush-ai playbook hand-off\` for the full playbook.
|
|
11498
11714
|
`;
|
|
11499
11715
|
function registerTaskCommand(program) {
|
|
11500
11716
|
const task = program.command("task").description("Create and manage tasks").addHelpText("after", TASK_HELP_AFTER);
|
|
@@ -11844,7 +12060,7 @@ function registerTaskCommand(program) {
|
|
|
11844
12060
|
}
|
|
11845
12061
|
if (options.last) {
|
|
11846
12062
|
const n = parseInt(options.last, 10);
|
|
11847
|
-
if (!isNaN(n) && n > 0) {
|
|
12063
|
+
if (!Number.isNaN(n) && n > 0) {
|
|
11848
12064
|
messages = messages.slice(-n);
|
|
11849
12065
|
}
|
|
11850
12066
|
}
|
|
@@ -12061,6 +12277,7 @@ function registerCommands(program) {
|
|
|
12061
12277
|
registerAgentCommand(program);
|
|
12062
12278
|
registerTaskCommand(program);
|
|
12063
12279
|
registerSkillCommand(program);
|
|
12280
|
+
registerPlaybookCommand(program);
|
|
12064
12281
|
registerCheckCommand(program);
|
|
12065
12282
|
registerMcpCommand(program);
|
|
12066
12283
|
registerMarketplaceCommand(program);
|
|
@@ -12139,7 +12356,7 @@ var BANNER = `
|
|
|
12139
12356
|
${chalk7.dim("\xB7")} From Cursor / Claude Code, hand off a task to a Rush agent without restating context.
|
|
12140
12357
|
${chalk7.dim("\xB7")} Compose workflows where a Rush specialist agent runs as your sub-agent.
|
|
12141
12358
|
|
|
12142
|
-
${chalk7.dim("For agents:")} ${chalk7.cyan("rush-ai
|
|
12359
|
+
${chalk7.dim("For agents:")} ${chalk7.cyan("rush-ai playbook")} prints ready-to-consume usage playbooks.
|
|
12143
12360
|
`;
|
|
12144
12361
|
function showWelcomeGuide() {
|
|
12145
12362
|
const lines = [
|
|
@@ -12151,11 +12368,12 @@ function showWelcomeGuide() {
|
|
|
12151
12368
|
` ${chalk7.cyan("rush-ai auth login")} Log in to the Rush platform`,
|
|
12152
12369
|
` ${chalk7.cyan("rush-ai agent list")} Browse available agents`,
|
|
12153
12370
|
` ${chalk7.cyan("rush-ai task create -a web-builder")} Hand a task to a Rush agent`,
|
|
12371
|
+
` ${chalk7.cyan("rush-ai skill install <skill>")} Install a Skill through Rush auth`,
|
|
12154
12372
|
"",
|
|
12155
12373
|
chalk7.dim(" For AI agents:"),
|
|
12156
|
-
` ${chalk7.cyan("rush-ai
|
|
12157
|
-
` ${chalk7.cyan("rush-ai
|
|
12158
|
-
` ${chalk7.cyan("rush-ai
|
|
12374
|
+
` ${chalk7.cyan("rush-ai playbook")} Print usage playbooks (markdown)`,
|
|
12375
|
+
` ${chalk7.cyan("rush-ai playbook hand-off")} IDE \u2192 Rush task hand-off`,
|
|
12376
|
+
` ${chalk7.cyan("rush-ai playbook agent-shelf")} Calling a Rush agent as a sub-agent`,
|
|
12159
12377
|
""
|
|
12160
12378
|
];
|
|
12161
12379
|
try {
|