skiller 0.7.2 → 0.7.3

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.
@@ -38,8 +38,10 @@ exports.syncMdcToSkillMd = syncMdcToSkillMd;
38
38
  exports.discoverSkills = discoverSkills;
39
39
  exports.getSkillsGitignorePaths = getSkillsGitignorePaths;
40
40
  exports.propagateSkills = propagateSkills;
41
+ exports.migrateRulesToSkills = migrateRulesToSkills;
41
42
  exports.copySkillFoldersFromRules = copySkillFoldersFromRules;
42
43
  exports.copyMdcFilesFromRules = copyMdcFilesFromRules;
44
+ exports.deleteRulesDir = deleteRulesDir;
43
45
  const path = __importStar(require("path"));
44
46
  const fs = __importStar(require("fs/promises"));
45
47
  const yaml = __importStar(require("js-yaml"));
@@ -445,6 +447,27 @@ async function findSkillFoldersInRules(dir, depth = 0) {
445
447
  }
446
448
  return skillFolders;
447
449
  }
450
+ /**
451
+ * Migrates all content from .claude/rules to .claude/skills and deletes the rules directory.
452
+ * This is the main entry point for rules migration - it only processes if rules directory exists.
453
+ */
454
+ async function migrateRulesToSkills(skillerDir, verbose, dryRun) {
455
+ const rulesDir = path.join(skillerDir, 'rules');
456
+ // Check if rules directory exists - early exit if not
457
+ try {
458
+ await fs.access(rulesDir);
459
+ }
460
+ catch {
461
+ // No rules directory - nothing to migrate
462
+ return;
463
+ }
464
+ // Copy skill folders (folders with SKILL.md)
465
+ await copySkillFoldersFromRules(skillerDir, verbose, dryRun);
466
+ // Copy standalone .mdc files
467
+ await copyMdcFilesFromRules(skillerDir, verbose, dryRun);
468
+ // Delete the rules directory after migration
469
+ await deleteRulesDir(skillerDir, verbose, dryRun);
470
+ }
448
471
  /**
449
472
  * Copies skill folders (folders containing SKILL.md) from .claude/rules to .claude/skills.
450
473
  * This allows users to organize skills in the rules directory and have them automatically
@@ -507,13 +530,41 @@ async function copyMdcFilesFromRules(skillerDir, verbose, dryRun) {
507
530
  const targetDir = path.join(skillsDir, skillName);
508
531
  const targetPath = path.join(targetDir, mdcFile.name);
509
532
  try {
533
+ const content = await fs.readFile(sourcePath, 'utf8');
534
+ // Parse and clean frontmatter - remove globs and alwaysApply: false
535
+ const { frontmatter, body } = (0, FrontmatterParser_1.parseFrontmatter)(content);
536
+ let cleanedContent;
537
+ if (frontmatter && Object.keys(frontmatter).length > 0) {
538
+ const cleanedFrontmatter = {};
539
+ // Only keep description and alwaysApply: true
540
+ if (frontmatter.description) {
541
+ cleanedFrontmatter.description = frontmatter.description;
542
+ }
543
+ if (frontmatter.alwaysApply === true) {
544
+ cleanedFrontmatter.alwaysApply = true;
545
+ }
546
+ // Note: globs and alwaysApply: false are intentionally omitted
547
+ if (Object.keys(cleanedFrontmatter).length > 0) {
548
+ cleanedContent = `---
549
+ ${yaml.dump(cleanedFrontmatter, { lineWidth: -1, noRefs: true }).trim()}
550
+ ---
551
+
552
+ ${body}
553
+ `;
554
+ }
555
+ else {
556
+ cleanedContent = body;
557
+ }
558
+ }
559
+ else {
560
+ cleanedContent = content;
561
+ }
510
562
  if (dryRun) {
511
563
  (0, constants_1.logVerboseInfo)(`DRY RUN: Would copy ${mdcFile.name} from rules to skills/${skillName}/${mdcFile.name}`, verbose, dryRun);
512
564
  }
513
565
  else {
514
566
  await fs.mkdir(targetDir, { recursive: true });
515
- const content = await fs.readFile(sourcePath, 'utf8');
516
- await fs.writeFile(targetPath, content, 'utf8');
567
+ await fs.writeFile(targetPath, cleanedContent, 'utf8');
517
568
  (0, constants_1.logVerboseInfo)(`Copied ${mdcFile.name} from rules to skills/${skillName}/${mdcFile.name}`, verbose, dryRun);
518
569
  }
519
570
  copiedNames.push(skillName);
@@ -527,3 +578,30 @@ async function copyMdcFilesFromRules(skillerDir, verbose, dryRun) {
527
578
  }
528
579
  return copiedNames;
529
580
  }
581
+ /**
582
+ * Deletes the .claude/rules directory after content has been migrated to .claude/skills.
583
+ * This completes the migration from the old rules-based structure to the new skills-based structure.
584
+ */
585
+ async function deleteRulesDir(skillerDir, verbose, dryRun) {
586
+ const rulesDir = path.join(skillerDir, 'rules');
587
+ // Check if rules directory exists
588
+ try {
589
+ await fs.access(rulesDir);
590
+ }
591
+ catch {
592
+ return false; // No rules directory to delete
593
+ }
594
+ if (dryRun) {
595
+ (0, constants_1.logVerboseInfo)(`DRY RUN: Would delete .claude/rules directory after migration`, verbose, dryRun);
596
+ return true;
597
+ }
598
+ try {
599
+ await fs.rm(rulesDir, { recursive: true, force: true });
600
+ (0, constants_1.logVerboseInfo)(`Deleted .claude/rules directory after migration to .claude/skills`, verbose, dryRun);
601
+ return true;
602
+ }
603
+ catch (err) {
604
+ (0, constants_1.logWarn)(`Failed to delete .claude/rules: ${err.message}`, dryRun);
605
+ return false;
606
+ }
607
+ }
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.hasSkillMd = hasSkillMd;
37
+ exports.hasAlwaysApplyMdc = hasAlwaysApplyMdc;
37
38
  exports.isGroupingDir = isGroupingDir;
38
39
  exports.walkSkillsTree = walkSkillsTree;
39
40
  exports.formatValidationWarnings = formatValidationWarnings;
@@ -42,6 +43,7 @@ exports.copySkillsDirectoryWithTransform = copySkillsDirectoryWithTransform;
42
43
  const path = __importStar(require("path"));
43
44
  const fs = __importStar(require("fs/promises"));
44
45
  const constants_1 = require("../constants");
46
+ const FrontmatterParser_1 = require("./FrontmatterParser");
45
47
  /**
46
48
  * Checks if a directory contains a SKILL.md file.
47
49
  */
@@ -55,6 +57,30 @@ async function hasSkillMd(dirPath) {
55
57
  return false;
56
58
  }
57
59
  }
60
+ /**
61
+ * Checks if a directory contains an .mdc file with alwaysApply: true.
62
+ * These directories are valid without SKILL.md since alwaysApply rules
63
+ * are Cursor-style rules, not Claude Code skills.
64
+ */
65
+ async function hasAlwaysApplyMdc(dirPath) {
66
+ try {
67
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
68
+ for (const entry of entries) {
69
+ if (entry.isFile() && entry.name.endsWith('.mdc')) {
70
+ const mdcPath = path.join(dirPath, entry.name);
71
+ const content = await fs.readFile(mdcPath, 'utf8');
72
+ const { frontmatter } = (0, FrontmatterParser_1.parseFrontmatter)(content);
73
+ if (frontmatter?.alwaysApply === true) {
74
+ return true;
75
+ }
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+ catch {
81
+ return false;
82
+ }
83
+ }
58
84
  /**
59
85
  * Checks if a directory is a grouping directory (contains subdirectories with SKILL.md).
60
86
  */
@@ -121,8 +147,12 @@ async function walkSkillsTree(root) {
121
147
  await walk(entryPath, entryRelativePath, depth + 1);
122
148
  }
123
149
  else {
124
- // This is neither a skill nor a grouping directory - warn about it
125
- warnings.push(`Directory '${entryRelativePath}' in .claude/skills has no SKILL.md and contains no sub-skills. It may be malformed or stray.`);
150
+ // Check if this is a valid alwaysApply directory (no SKILL.md expected)
151
+ const hasAlwaysApply = await hasAlwaysApplyMdc(entryPath);
152
+ if (!hasAlwaysApply) {
153
+ // This is neither a skill nor a grouping directory - warn about it
154
+ warnings.push(`Directory '${entryRelativePath}' in .claude/skills has no SKILL.md and contains no sub-skills. It may be malformed or stray.`);
155
+ }
126
156
  }
127
157
  }
128
158
  }
package/dist/lib.js CHANGED
@@ -102,12 +102,11 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
102
102
  (0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose);
103
103
  // Propagate skills (or cleanup if disabled) - do this for each nested directory
104
104
  const skillsEnabledResolved = resolveSkillsEnabled(skillsEnabled, rootConfig.skills?.enabled);
105
- const { propagateSkills, copySkillFoldersFromRules, copyMdcFilesFromRules, } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
106
- // Copy skill folders and .mdc files from rules to skills
105
+ const { propagateSkills, migrateRulesToSkills } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
106
+ // Migrate content from .claude/rules to .claude/skills (only if rules exists)
107
107
  if (skillsEnabledResolved) {
108
108
  for (const configEntry of hierarchicalConfigs) {
109
- await copySkillFoldersFromRules(configEntry.skillerDir, verbose, dryRun);
110
- await copyMdcFilesFromRules(configEntry.skillerDir, verbose, dryRun);
109
+ await migrateRulesToSkills(configEntry.skillerDir, verbose, dryRun);
111
110
  }
112
111
  }
113
112
  // Propagate skills for each nested .claude directory (or cleanup if disabled)
@@ -131,11 +130,10 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
131
130
  (0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose);
132
131
  // Propagate skills (or cleanup if disabled)
133
132
  const skillsEnabledResolved = resolveSkillsEnabled(skillsEnabled, singleConfig.config.skills?.enabled);
134
- const { propagateSkills, copySkillFoldersFromRules, copyMdcFilesFromRules, } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
135
- // Copy skill folders and .mdc files from rules to skills
133
+ const { propagateSkills, migrateRulesToSkills } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
134
+ // Migrate content from .claude/rules to .claude/skills (only if rules exists)
136
135
  if (skillsEnabledResolved) {
137
- await copySkillFoldersFromRules(singleConfig.skillerDir, verbose, dryRun);
138
- await copyMdcFilesFromRules(singleConfig.skillerDir, verbose, dryRun);
136
+ await migrateRulesToSkills(singleConfig.skillerDir, verbose, dryRun);
139
137
  }
140
138
  // Always call propagateSkills - it handles cleanup when disabled
141
139
  await propagateSkills(projectRoot, selectedAgents, skillsEnabledResolved, verbose, dryRun, singleConfig.skillerDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skiller",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Skiller — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "publishConfig": {