joyskills-cli 0.3.6 → 0.3.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joyskills-cli",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "JoySkills CLI v2.0 - Multi-agent skill management with JoyCode native support",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -236,10 +236,11 @@ async function resolveAndInstall(skillInput, targetDir, projectRoot, options) {
236
236
  const pathPart = skillInput.slice(7); // Remove team://
237
237
  const parts = pathPart.split('/');
238
238
  const registryName = parts[0];
239
- const skillName = parts[1];
239
+ // Support sub-path skills: team://registry/subdir/skill-name
240
+ const skillName = parts.slice(1).join('/');
240
241
 
241
242
  if (!registryName || !skillName) {
242
- throw new Error(`Invalid team:// URL format: ${skillInput}. Expected: team://<registry>/<skill>`);
243
+ throw new Error(`Invalid team:// URL format: ${skillInput}. Expected: team://<registry>/<skill> or team://<registry>/<subdir>/<skill>`);
243
244
  }
244
245
 
245
246
  // Get registry path with auto-prompt for missing registries
@@ -386,7 +387,8 @@ async function tryInstallFromRegistryDir(skillName, registryDirPath, targetDir,
386
387
 
387
388
  if (!skill) return false;
388
389
 
389
- const targetPath = path.join(targetDir, skillName);
390
+ // Use skill.name for target path to maintain subdirectory structure
391
+ const targetPath = path.join(targetDir, skill.name);
390
392
  copyRecursive(skill.path, targetPath);
391
393
 
392
394
  // Get git commit for the skill source
@@ -396,7 +398,7 @@ async function tryInstallFromRegistryDir(skillName, registryDirPath, targetDir,
396
398
  if (!options.global) {
397
399
  const lockfileManager = new LockfileManager(projectRoot);
398
400
  await lockfileManager.load();
399
- lockfileManager.updateSkill(skillName, {
401
+ lockfileManager.updateSkill(skill.name, {
400
402
  version: skill.version || '1.0.0',
401
403
  source: sourceLabel,
402
404
  commit: commitHash,
@@ -405,7 +407,7 @@ async function tryInstallFromRegistryDir(skillName, registryDirPath, targetDir,
405
407
  await lockfileManager.save();
406
408
  }
407
409
 
408
- console.log(chalk.green(`✅ Installed ${skillName} v${skill.version || '1.0.0'} from ${sourceLabel}`));
410
+ console.log(chalk.green(`✅ Installed ${skill.name} v${skill.version || '1.0.0'} from ${sourceLabel}`));
409
411
  return true;
410
412
  }
411
413
  }
@@ -595,7 +597,9 @@ async function installFromGitUrl(gitUrl, targetDir, options, subPath = '') {
595
597
  }
596
598
 
597
599
  for (const skill of selectedSkills) {
598
- await installSkillFiles(skill, targetDir);
600
+ // For Git URL installs with subPath, use the last part of subPath as target name
601
+ const targetName = subPath ? subPath.split('/').pop() : skill.name;
602
+ await installSkillFiles(skill, targetDir, targetName);
599
603
  }
600
604
 
601
605
  // Update lockfile only for project-level installation (npm-like behavior)
@@ -603,7 +607,8 @@ async function installFromGitUrl(gitUrl, targetDir, options, subPath = '') {
603
607
  const lockfileManager = new LockfileManager(process.cwd());
604
608
  await lockfileManager.load();
605
609
  for (const skill of selectedSkills) {
606
- lockfileManager.updateSkill(skill.name, {
610
+ const targetName = subPath ? subPath.split('/').pop() : skill.name;
611
+ lockfileManager.updateSkill(targetName, {
607
612
  version: skill.version || '1.0.0',
608
613
  source: 'git',
609
614
  repository: gitUrl,
@@ -689,7 +694,10 @@ async function installFromGitHub(owner, repo, skillPath, targetDir, options) {
689
694
 
690
695
  // Install selected skills
691
696
  for (const skill of selectedSkills) {
692
- await installSkillFiles(skill, targetDir);
697
+ // For GitHub installs, use the last part of skillPath as target name
698
+ // e.g., "anthropics/skills/pdf" -> install as "pdf", not "skills/pdf"
699
+ const targetName = skillPath ? skillPath.split('/').pop() : skill.name;
700
+ await installSkillFiles(skill, targetDir, targetName);
693
701
  }
694
702
 
695
703
  // Update lockfile only for project-level installation (npm-like behavior)
@@ -697,7 +705,9 @@ async function installFromGitHub(owner, repo, skillPath, targetDir, options) {
697
705
  const lockfileManager = new LockfileManager(process.cwd());
698
706
  await lockfileManager.load();
699
707
  for (const skill of selectedSkills) {
700
- lockfileManager.updateSkill(skill.name, {
708
+ // Use the same targetName for lockfile consistency
709
+ const targetName = skillPath ? skillPath.split('/').pop() : skill.name;
710
+ lockfileManager.updateSkill(targetName, {
701
711
  version: skill.version || '1.0.0',
702
712
  source: 'github',
703
713
  repository: `${owner}/${repo}`,
@@ -846,7 +856,7 @@ export async function findSkills(basePath) {
846
856
  const { name, description, version } = parseSkillMd(skillMdContent);
847
857
 
848
858
  skills.push({
849
- name: relativePath.split('/').pop() || name || path.basename(dir),
859
+ name: relativePath || name || path.basename(dir),
850
860
  path: skillPath,
851
861
  relativePath,
852
862
  description,
@@ -902,8 +912,8 @@ function formatSize(bytes) {
902
912
  return (bytes / (1024 * 1024)).toFixed(1) + 'MB';
903
913
  }
904
914
 
905
- async function installSkillFiles(skill, targetDir) {
906
- const targetPath = path.join(targetDir, skill.name);
915
+ async function installSkillFiles(skill, targetDir, targetName = null) {
916
+ const targetPath = path.join(targetDir, targetName || skill.name);
907
917
 
908
918
  if (fs.existsSync(targetPath)) {
909
919
  fs.rmSync(targetPath, { recursive: true, force: true });