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/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 });
@@ -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 ? resolve10(dirname4(mktDir), target) : null;
4772
- if (resolvedTarget && resolvedTarget === resolve10(src.rootDir)) {
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 = resolve10(plugin.sourceDir, cap);
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 basename2,
5555
+ basename as basename3,
5475
5556
  dirname as dirname6,
5476
5557
  extname,
5477
5558
  relative as pathRelative,
5478
- resolve as resolve11
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 = resolve11(versionDir, srcMcp);
6200
+ const destPath = resolve12(versionDir, srcMcp);
6120
6201
  files.push(destPath);
6121
- const srcPath = resolve11(plugin.sourceDir, srcMcp);
6122
- const rel = pathRelative(resolve11(plugin.sourceDir), srcPath);
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 = resolve11(sourceDir, relativeRef);
6268
- const rel = pathRelative(resolve11(sourceDir), srcPath);
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 = resolve11(versionDir, relativeRef);
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 = basename2(input.pluginDir);
6371
+ const pluginBase = basename3(input.pluginDir);
6291
6372
  await mkdir7(pluginParent, { recursive: true });
6292
6373
  const tmpPluginDir = await mkdtemp(
6293
- resolve11(pluginParent, `.${pluginBase}.tmp-`)
6374
+ resolve12(pluginParent, `.${pluginBase}.tmp-`)
6294
6375
  );
6295
6376
  const backupPluginDir = await mkdtemp(
6296
- resolve11(pluginParent, `.${pluginBase}.bak-`)
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
- resolve11(pluginParent, `.${pluginBase}.failed-`)
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
- resolve11(plugin.sourceDir, "commands"),
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 = resolve11(plugin.sourceDir, "skills");
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: resolve11(nativeSkillsDir, name),
6514
+ sourceDir: resolve12(nativeSkillsDir, name),
6434
6515
  skillName: name
6435
6516
  });
6436
6517
  }
6437
6518
  } else {
6438
- const dotSkillsDir = resolve11(plugin.sourceDir, ".skills");
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, resolve11(dstSkills, source.skillName), {
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
- resolve11(dstSkills, source.skillName, "SKILL.md"),
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(resolve11(skillsDir, entry.name, "SKILL.md"))) {
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 = resolve11(current, entry.name);
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 ?? basename2(filePath, extname(filePath))
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 = resolve11(
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 = resolve11(baseDir, entry);
6701
- const manifestPath = resolve11(versionDir, ".codex-plugin", "plugin.json");
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 resolve14 } from "path";
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 resolve12 } from "path";
6942
+ import { resolve as resolve13 } from "path";
6862
6943
  var CURSOR_RELATIVE_DIR = ".cursor";
6863
6944
  function cursorDir(home) {
6864
- return resolve12(home, CURSOR_RELATIVE_DIR);
6945
+ return resolve13(home, CURSOR_RELATIVE_DIR);
6865
6946
  }
6866
6947
  function cursorMcpJsonPath(home) {
6867
- return resolve12(cursorDir(home), "mcp.json");
6948
+ return resolve13(cursorDir(home), "mcp.json");
6868
6949
  }
6869
6950
  function cursorRulesDir(home) {
6870
- return resolve12(cursorDir(home), "rules");
6951
+ return resolve13(cursorDir(home), "rules");
6871
6952
  }
6872
6953
  function cursorRuleMdcPath(home, ruleName) {
6873
- return resolve12(cursorRulesDir(home), `${ruleName}.mdc`);
6954
+ return resolve13(cursorRulesDir(home), `${ruleName}.mdc`);
6874
6955
  }
6875
6956
  function cursorSkillsDir(home) {
6876
- return resolve12(cursorDir(home), "skills");
6957
+ return resolve13(cursorDir(home), "skills");
6877
6958
  }
6878
6959
  function cursorSkillDir(home, skillName) {
6879
- return resolve12(cursorSkillsDir(home), skillName);
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 resolve13 } from "path";
7071
+ import { resolve as resolve14 } from "path";
6991
7072
  async function readSkillDescription(skillSourceDir) {
6992
- const skillMdPath = resolve13(skillSourceDir, "SKILL.md");
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 = resolve14(plugin.sourceDir, "skills");
7436
+ const skillsSourceDir = resolve15(plugin.sourceDir, "skills");
7356
7437
  const skillDirs = await listSkillDirs(skillsSourceDir);
7357
7438
  for (const skillName of skillDirs) {
7358
- const srcDir = resolve14(skillsSourceDir, skillName);
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 = resolve14(plugin.sourceDir, "rules");
7471
+ const rulesSourceDir = resolve15(plugin.sourceDir, "rules");
7391
7472
  const rulesFiles = await listRuleMdFiles(rulesSourceDir);
7392
7473
  for (const ruleFile of rulesFiles) {
7393
- const srcPath = resolve14(rulesSourceDir, ruleFile);
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 resolve16 } from "path";
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 resolve15 } from "path";
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 : resolve15(linkPath, "..", 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 = 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);
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 = resolve15(home, LEGACY_ASSET_MANIFEST_RELATIVE);
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 = resolve16(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);
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 = resolve16(home, LEGACY_SKILL_SYMLINK_RELATIVE);
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 = resolve16(home, CLAUDE_SETTINGS_JSON_RELATIVE);
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 resolve17 } from "path";
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 resolve17(home, MIGRATION_LOG_RELATIVE_PATH);
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 resolve18 } from "path";
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(resolve18(sourceDir, "commands"), ".md");
7960
+ return dirHasFileWithExt(resolve19(sourceDir, "commands"), ".md");
7880
7961
  }
7881
7962
  async function hasSkills(sourceDir) {
7882
- const skillsDir = resolve18(sourceDir, "skills");
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 = resolve18(skillsDir, entry.name, "SKILL.md");
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(resolve18(sourceDir, "rules"), ".md");
7982
+ return dirHasFileWithExt(resolve19(sourceDir, "rules"), ".md");
7902
7983
  }
7903
7984
  async function hasHooks(sourceDir) {
7904
- const hooksDir = resolve18(sourceDir, "hooks");
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 = resolve18(sourceDir, ".skills");
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 = resolve18(dirPath, entry.name);
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 resolve19 } from "path";
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 = resolve19(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);
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 = resolve20(marketplace.rootDir, "plugins", ref.name);
8955
- const manifestPath = resolve20(pluginDir, ".claude-plugin/plugin.json");
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 { 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.";
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
- $ 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
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
- 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.
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
- 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;
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 null;
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 listSkills(dir) {
9864
- return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => basename3(f, ".md")).sort();
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 [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);
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 available = listSkills(dir);
9877
- if (opts.list) {
9878
- for (const s of available) output.log(s);
10063
+ const stripped = stripRushOnlyFlags(initialArgs);
10064
+ if (stripped.args.length === 0) {
10065
+ command.help();
9879
10066
  return;
9880
10067
  }
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)"}`);
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
- process.stdout.write(readFileSync7(file2, "utf-8"));
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
- output.log(` ${truncateText(tp.text)}`);
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 skill hand-off\` for the full playbook.
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 skill")} prints ready-to-consume usage playbooks.
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 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`,
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 {