skiller 0.7.11 → 0.7.12

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.
@@ -49,6 +49,92 @@ const yaml = __importStar(require("js-yaml"));
49
49
  const constants_1 = require("../constants");
50
50
  const SkillsUtils_1 = require("./SkillsUtils");
51
51
  const FrontmatterParser_1 = require("./FrontmatterParser");
52
+ /**
53
+ * For non-Claude agents, compile a wrapper SKILL.md (body is a single @reference)
54
+ * into a standalone SKILL.md with the referenced file's body inlined.
55
+ *
56
+ * We intentionally keep this conservative: only expand when the body is *just*
57
+ * an @reference line, to avoid accidentally treating email addresses or
58
+ * "@mentions" inside real content as file references.
59
+ */
60
+ async function compileSkillMdForNonClaudeAgents(skillMdContent, projectRoot, skillFolderPath) {
61
+ const { frontmatter, rawFrontmatter, body } = (0, FrontmatterParser_1.parseFrontmatter)(skillMdContent);
62
+ const refCheck = isReferenceBody(body);
63
+ if (!refCheck.isReference || !refCheck.referencePath) {
64
+ return skillMdContent;
65
+ }
66
+ const referencePath = refCheck.referencePath;
67
+ const absoluteRefPath = referencePath.startsWith('./') || referencePath.startsWith('../')
68
+ ? path.resolve(skillFolderPath, referencePath)
69
+ : path.resolve(projectRoot, referencePath);
70
+ // Security: only inline references within the project root.
71
+ const normalizedProjectRoot = path.resolve(projectRoot);
72
+ const normalizedAbsoluteRefPath = path.resolve(absoluteRefPath);
73
+ if (!normalizedAbsoluteRefPath.startsWith(normalizedProjectRoot + path.sep)) {
74
+ return skillMdContent;
75
+ }
76
+ let referencedContent;
77
+ try {
78
+ referencedContent = await fs.readFile(normalizedAbsoluteRefPath, 'utf8');
79
+ }
80
+ catch {
81
+ return skillMdContent;
82
+ }
83
+ const { body: referencedBody } = (0, FrontmatterParser_1.parseFrontmatter)(referencedContent);
84
+ const fmData = rawFrontmatter && Object.keys(rawFrontmatter).length > 0
85
+ ? rawFrontmatter
86
+ : frontmatter && Object.keys(frontmatter).length > 0
87
+ ? frontmatter
88
+ : null;
89
+ if (fmData) {
90
+ return `---
91
+ ${yaml.dump(fmData, { lineWidth: -1, noRefs: true }).trim()}
92
+ ---
93
+
94
+ ${referencedBody}
95
+ `;
96
+ }
97
+ return `${referencedBody}\n`;
98
+ }
99
+ /**
100
+ * Copies a single skill directory to an agent skill directory:
101
+ * - SKILL.md is compiled (inlines @reference wrapper content)
102
+ * - .mdc files are excluded (Claude-only sources)
103
+ * - all other files are copied as-is
104
+ */
105
+ async function copySkillDirectoryForNonClaudeAgents(src, dest, projectRoot, skillFolderPath, depth = 0) {
106
+ // Security: Prevent DoS via deeply nested directories
107
+ if (depth >= constants_1.MAX_RECURSION_DEPTH) {
108
+ return;
109
+ }
110
+ const stat = await fs.stat(src);
111
+ if (stat.isDirectory()) {
112
+ await fs.mkdir(dest, { recursive: true });
113
+ const entries = await fs.readdir(src, { withFileTypes: true });
114
+ for (const entry of entries) {
115
+ // Exclude all .mdc files from agent skills directories.
116
+ if (entry.isFile() && entry.name.endsWith('.mdc')) {
117
+ continue;
118
+ }
119
+ const srcPath = path.join(src, entry.name);
120
+ const destPath = path.join(dest, entry.name);
121
+ await copySkillDirectoryForNonClaudeAgents(srcPath, destPath, projectRoot, skillFolderPath, depth + 1);
122
+ }
123
+ return;
124
+ }
125
+ // Files
126
+ if (path.basename(src) === constants_1.SKILL_MD_FILENAME) {
127
+ const content = await fs.readFile(src, 'utf8');
128
+ const compiled = await compileSkillMdForNonClaudeAgents(content, projectRoot, skillFolderPath);
129
+ await fs.writeFile(dest, compiled, 'utf8');
130
+ return;
131
+ }
132
+ // Extra guard: skip .mdc even if reached via recursion.
133
+ if (src.endsWith('.mdc')) {
134
+ return;
135
+ }
136
+ await fs.copyFile(src, dest);
137
+ }
52
138
  /**
53
139
  * Check if SKILL.md body is just a reference (single non-empty line starting with @).
54
140
  * This replaces the previous synced: true frontmatter detection.
@@ -373,7 +459,7 @@ async function discoverSkills(projectRoot, skillerDir) {
373
459
  * Copies skills from source directory to target agent's skills directory.
374
460
  * Validates skill structure and returns copy count and warnings.
375
461
  */
376
- async function copySkillsToAgent(sourceSkillsDir, targetSkillsDir, verbose, dryRun) {
462
+ async function copySkillsToAgent(sourceSkillsDir, targetSkillsDir, projectRoot, verbose, dryRun) {
377
463
  const warnings = [];
378
464
  let copied = 0;
379
465
  try {
@@ -402,7 +488,7 @@ async function copySkillsToAgent(sourceSkillsDir, targetSkillsDir, verbose, dryR
402
488
  const relativeSkillPath = path.relative(sourceSkillsDir, skill.path);
403
489
  const targetSkillPath = path.join(targetSkillsDir, relativeSkillPath);
404
490
  if (!dryRun) {
405
- await (0, SkillsUtils_1.copySkillsDirectory)(skillPath, targetSkillPath);
491
+ await copySkillDirectoryForNonClaudeAgents(skillPath, targetSkillPath, projectRoot, skillPath);
406
492
  }
407
493
  (0, constants_1.logVerboseInfo)(dryRun
408
494
  ? `DRY RUN: Would copy skill '${skill.name}' to ${targetSkillsDir}`
@@ -492,7 +578,7 @@ async function propagateSkills(projectRoot, agents, skillsEnabled, verbose, dryR
492
578
  }
493
579
  // Copy skills to each unique destination
494
580
  for (const targetPath of destinationPaths) {
495
- const result = await copySkillsToAgent(skillsDir, targetPath, verbose, dryRun);
581
+ const result = await copySkillsToAgent(skillsDir, targetPath, projectRoot, verbose, dryRun);
496
582
  if (result.copied > 0) {
497
583
  (0, constants_1.logVerboseInfo)(`Copied ${result.copied} skill(s) to ${targetPath}`, verbose, dryRun);
498
584
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skiller",
3
- "version": "0.7.11",
3
+ "version": "0.7.12",
4
4
  "description": "Skiller — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "publishConfig": {