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/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 skill agent-shelf\` for the full playbook.
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
- const isValid = Array.isArray(candidates) && candidates.length > 0 && candidates.every(
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 + "\n");
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, "\n" + script + "\n");
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 resolve20 } from "path";
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 resolve10 } from "path";
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 resolve9 } from "path";
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 resolve9(this.home, CLAUDE_DIR);
4464
+ return resolve10(this.home, CLAUDE_DIR);
4384
4465
  }
4385
4466
  /** `<home>/.claude/settings.json` */
4386
4467
  get settingsJson() {
4387
- return resolve9(this.claudeDir, "settings.json");
4468
+ return resolve10(this.claudeDir, "settings.json");
4388
4469
  }
4389
4470
  /** `<home>/.claude/plugins/` */
4390
4471
  get pluginsDir() {
4391
- return resolve9(this.claudeDir, PLUGINS_SUBDIR);
4472
+ return resolve10(this.claudeDir, PLUGINS_SUBDIR);
4392
4473
  }
4393
4474
  /** `<home>/.claude/plugins/known_marketplaces.json` */
4394
4475
  get knownMarketplacesJson() {
4395
- return resolve9(this.pluginsDir, "known_marketplaces.json");
4476
+ return resolve10(this.pluginsDir, "known_marketplaces.json");
4396
4477
  }
4397
4478
  /** `<home>/.claude/plugins/installed_plugins.json` */
4398
4479
  get installedPluginsJson() {
4399
- return resolve9(this.pluginsDir, "installed_plugins.json");
4480
+ return resolve10(this.pluginsDir, "installed_plugins.json");
4400
4481
  }
4401
4482
  /** `<home>/.claude/plugins/cache/` */
4402
4483
  get cacheDir() {
4403
- return resolve9(this.pluginsDir, CACHE_SUBDIR);
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 resolve9(this.pluginsDir, "marketplaces");
4494
+ return resolve10(this.pluginsDir, "marketplaces");
4414
4495
  }
4415
4496
  /** `<home>/.claude/plugins/marketplaces/<mkt>/` */
4416
4497
  marketplaceInstallDir(marketplace) {
4417
- return resolve9(this.marketplacesRootDir, marketplace);
4498
+ return resolve10(this.marketplacesRootDir, marketplace);
4418
4499
  }
4419
4500
  /** `<home>/.claude/plugins/cache/<mkt>/` */
4420
4501
  marketplaceCacheDir(marketplace) {
4421
- return resolve9(this.cacheDir, marketplace);
4502
+ return resolve10(this.cacheDir, marketplace);
4422
4503
  }
4423
4504
  /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/` */
4424
4505
  pluginCacheDir(ref) {
4425
- return resolve9(this.marketplaceCacheDir(ref.marketplace), ref.name);
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 resolve9(this.pluginCacheDir(ref), version);
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 resolve9(
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 resolve9(this.pluginVersionDir(ref, version), capability);
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 = resolve10(plugin.sourceDir, cap);
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 ? resolve10(dirname4(mktDir), target) : null;
4772
- if (resolvedTarget && resolvedTarget === resolve10(src.rootDir)) {
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 = resolve10(plugin.sourceDir, cap);
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 = 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 basename2,
5581
+ basename as basename3,
5475
5582
  dirname as dirname6,
5476
5583
  extname,
5477
5584
  relative as pathRelative,
5478
- resolve as resolve11
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 = resolve11(versionDir, srcMcp);
6226
+ const destPath = resolve12(versionDir, srcMcp);
6120
6227
  files.push(destPath);
6121
- const srcPath = resolve11(plugin.sourceDir, srcMcp);
6122
- const rel = pathRelative(resolve11(plugin.sourceDir), srcPath);
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 = resolve11(sourceDir, relativeRef);
6268
- const rel = pathRelative(resolve11(sourceDir), srcPath);
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 = resolve11(versionDir, relativeRef);
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 = basename2(input.pluginDir);
6397
+ const pluginBase = basename3(input.pluginDir);
6291
6398
  await mkdir7(pluginParent, { recursive: true });
6292
6399
  const tmpPluginDir = await mkdtemp(
6293
- resolve11(pluginParent, `.${pluginBase}.tmp-`)
6400
+ resolve12(pluginParent, `.${pluginBase}.tmp-`)
6294
6401
  );
6295
6402
  const backupPluginDir = await mkdtemp(
6296
- resolve11(pluginParent, `.${pluginBase}.bak-`)
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
- resolve11(pluginParent, `.${pluginBase}.failed-`)
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
- resolve11(plugin.sourceDir, "commands"),
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 = resolve11(plugin.sourceDir, "skills");
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: resolve11(nativeSkillsDir, name),
6540
+ sourceDir: resolve12(nativeSkillsDir, name),
6434
6541
  skillName: name
6435
6542
  });
6436
6543
  }
6437
6544
  } else {
6438
- const dotSkillsDir = resolve11(plugin.sourceDir, ".skills");
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, resolve11(dstSkills, source.skillName), {
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
- resolve11(dstSkills, source.skillName, "SKILL.md"),
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(resolve11(skillsDir, entry.name, "SKILL.md"))) {
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 = resolve11(current, entry.name);
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 ?? basename2(filePath, extname(filePath))
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 = resolve11(
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 = resolve11(baseDir, entry);
6701
- const manifestPath = resolve11(versionDir, ".codex-plugin", "plugin.json");
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 resolve14 } from "path";
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 resolve12 } from "path";
6968
+ import { resolve as resolve13 } from "path";
6862
6969
  var CURSOR_RELATIVE_DIR = ".cursor";
6863
6970
  function cursorDir(home) {
6864
- return resolve12(home, CURSOR_RELATIVE_DIR);
6971
+ return resolve13(home, CURSOR_RELATIVE_DIR);
6865
6972
  }
6866
6973
  function cursorMcpJsonPath(home) {
6867
- return resolve12(cursorDir(home), "mcp.json");
6974
+ return resolve13(cursorDir(home), "mcp.json");
6868
6975
  }
6869
6976
  function cursorRulesDir(home) {
6870
- return resolve12(cursorDir(home), "rules");
6977
+ return resolve13(cursorDir(home), "rules");
6871
6978
  }
6872
6979
  function cursorRuleMdcPath(home, ruleName) {
6873
- return resolve12(cursorRulesDir(home), `${ruleName}.mdc`);
6980
+ return resolve13(cursorRulesDir(home), `${ruleName}.mdc`);
6874
6981
  }
6875
6982
  function cursorSkillsDir(home) {
6876
- return resolve12(cursorDir(home), "skills");
6983
+ return resolve13(cursorDir(home), "skills");
6877
6984
  }
6878
6985
  function cursorSkillDir(home, skillName) {
6879
- return resolve12(cursorSkillsDir(home), skillName);
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 resolve13 } from "path";
7097
+ import { resolve as resolve14 } from "path";
6991
7098
  async function readSkillDescription(skillSourceDir) {
6992
- const skillMdPath = resolve13(skillSourceDir, "SKILL.md");
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 = resolve14(plugin.sourceDir, "skills");
7462
+ const skillsSourceDir = resolve15(plugin.sourceDir, "skills");
7356
7463
  const skillDirs = await listSkillDirs(skillsSourceDir);
7357
7464
  for (const skillName of skillDirs) {
7358
- const srcDir = resolve14(skillsSourceDir, skillName);
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 = resolve14(plugin.sourceDir, "rules");
7497
+ const rulesSourceDir = resolve15(plugin.sourceDir, "rules");
7391
7498
  const rulesFiles = await listRuleMdFiles(rulesSourceDir);
7392
7499
  for (const ruleFile of rulesFiles) {
7393
- const srcPath = resolve14(rulesSourceDir, ruleFile);
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 resolve16 } from "path";
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 resolve15 } from "path";
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 : resolve15(linkPath, "..", 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 = resolve15(home, LEGACY_ASSET_MANIFEST_RELATIVE);
7678
- const legacyAssetsDir = resolve15(home, LEGACY_ASSETS_DIR_RELATIVE);
7679
- const skillSymlinkPath = resolve15(home, LEGACY_SKILL_SYMLINK_RELATIVE);
7680
- const settingsJsonPath = resolve15(home, CLAUDE_SETTINGS_JSON_RELATIVE);
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 = resolve15(home, LEGACY_ASSET_MANIFEST_RELATIVE);
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 = resolve16(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
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 = resolve16(home, LEGACY_SKILL_SYMLINK_RELATIVE);
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 = resolve16(home, CLAUDE_SETTINGS_JSON_RELATIVE);
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 resolve17 } from "path";
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 resolve17(home, MIGRATION_LOG_RELATIVE_PATH);
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 resolve18 } from "path";
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(resolve18(sourceDir, "commands"), ".md");
7986
+ return dirHasFileWithExt(resolve19(sourceDir, "commands"), ".md");
7880
7987
  }
7881
7988
  async function hasSkills(sourceDir) {
7882
- const skillsDir = resolve18(sourceDir, "skills");
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 = resolve18(skillsDir, entry.name, "SKILL.md");
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(resolve18(sourceDir, "rules"), ".md");
8008
+ return dirHasFileWithExt(resolve19(sourceDir, "rules"), ".md");
7902
8009
  }
7903
8010
  async function hasHooks(sourceDir) {
7904
- const hooksDir = resolve18(sourceDir, "hooks");
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 = resolve18(sourceDir, ".skills");
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 = resolve18(dirPath, entry.name);
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 resolve19 } from "path";
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 = resolve19(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
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 = resolve20(marketplace.rootDir, "plugins", ref.name);
8955
- const manifestPath = resolve20(pluginDir, ".claude-plugin/plugin.json");
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 { existsSync as existsSync11, readdirSync, readFileSync as readFileSync7 } from "fs";
9835
- import { basename as basename3, resolve as resolve21 } from "path";
9836
- var SKILL_DESCRIPTION = "Print agent usage skills (hand-off, agent-shelf, ...) as raw markdown.";
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
- $ npx rush-ai skill Print the skills index
9840
- $ npx rush-ai skill hand-off Print the hand-off playbook
9841
- $ npx rush-ai skill agent-shelf Print the agent-shelf playbook
9842
- $ npx rush-ai skill --list List available skills
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
- Designed to be consumed by AI agents inside IDEs (Cursor, Claude Code,
9845
- etc). Output is raw markdown on stdout \u2014 pipe it, grep it, feed it to
9846
- your own context.
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
- function resolveSkillsDir() {
9849
- const baseDir = import.meta.dirname ?? __dirname;
9850
- if (!baseDir) return null;
9851
- const candidates = [
9852
- resolve21(baseDir, "skills"),
9853
- resolve21(baseDir, "..", "skills"),
9854
- resolve21(baseDir, "..", "..", "skills"),
9855
- resolve21(baseDir, "..", "..", "..", "skills"),
9856
- resolve21(baseDir, "..", "..", "..", "..", "skills")
9857
- ];
9858
- for (const p of candidates) {
9859
- if (existsSync11(p)) return p;
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 null;
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 listSkills(dir) {
9864
- return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename3(f, ".md")).sort();
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 [name]").description(SKILL_DESCRIPTION).addHelpText("after", SKILL_HELP_AFTER).option("--list", "List available skill names and exit").action((name, opts) => {
9868
- const dir = resolveSkillsDir();
9869
- if (!dir) {
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 available = listSkills(dir);
9877
- if (opts.list) {
9878
- for (const s of available) output.log(s);
10089
+ const stripped = stripRushOnlyFlags(initialArgs);
10090
+ if (stripped.args.length === 0) {
10091
+ command.help();
9879
10092
  return;
9880
10093
  }
9881
- const target = name ?? "README";
9882
- const file2 = resolve21(dir, `${target}.md`);
9883
- if (!existsSync11(file2)) {
9884
- output.error(`Unknown skill "${target}".`);
9885
- output.dim(`Available: ${available.join(", ") || "(none)"}`);
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
- process.stdout.write(readFileSync7(file2, "utf-8"));
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
- output.log(` ${truncateText(tp.text)}`);
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 skill hand-off\` for the full playbook.
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 skill")} prints ready-to-consume usage playbooks.
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 skill")} Print usage playbooks (markdown)`,
12157
- ` ${chalk7.cyan("rush-ai skill hand-off")} IDE \u2192 Rush task hand-off`,
12158
- ` ${chalk7.cyan("rush-ai skill agent-shelf")} Calling a Rush agent as a sub-agent`,
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 {