@intellectronica/ruler 0.3.32 → 0.3.34

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/README.md CHANGED
@@ -76,7 +76,7 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
76
76
  | Junie | `.junie/guidelines.md` | - | - |
77
77
  | AugmentCode | `.augment/rules/ruler_augment_instructions.md` | - | - |
78
78
  | Kilo Code | `AGENTS.md` | `.kilocode/mcp.json` | `.claude/skills/` |
79
- | OpenCode | `AGENTS.md` | `opencode.json` | `.opencode/skill/` |
79
+ | OpenCode | `AGENTS.md` | `opencode.json` | `.opencode/skills/` |
80
80
  | Goose | `.goosehints` | - | `.agents/skills/` |
81
81
  | Qwen Code | `AGENTS.md` | `.qwen/settings.json` | - |
82
82
  | RooCode | `AGENTS.md` | `.roo/mcp.json` | `.roo/skills/` |
@@ -578,7 +578,7 @@ Skills are specialized knowledge packages that extend AI agent capabilities with
578
578
  - **GitHub Copilot**: `.claude/skills/` (shared with Claude Code)
579
579
  - **Kilo Code**: `.claude/skills/` (shared with Claude Code)
580
580
  - **OpenAI Codex CLI**: `.codex/skills/`
581
- - **OpenCode**: `.opencode/skill/`
581
+ - **OpenCode**: `.opencode/skills/`
582
582
  - **Pi Coding Agent**: `.pi/skills/`
583
583
  - **Goose**: `.agents/skills/`
584
584
  - **Amp**: `.agents/skills/` (shared with Goose)
@@ -644,7 +644,7 @@ When skills support is enabled and gitignore integration is active, Ruler automa
644
644
 
645
645
  - `.claude/skills/` (for Claude Code, GitHub Copilot, and Kilo Code)
646
646
  - `.codex/skills/` (for OpenAI Codex CLI)
647
- - `.opencode/skill/` (for OpenCode)
647
+ - `.opencode/skills/` (for OpenCode)
648
648
  - `.pi/skills/` (for Pi Coding Agent)
649
649
  - `.agents/skills/` (for Goose and Amp)
650
650
  - `.agent/skills/` (for Antigravity)
@@ -703,7 +703,7 @@ ruler apply
703
703
  # 3. Skills are now available to compatible agents:
704
704
  # - Claude Code, GitHub Copilot & Kilo Code: .claude/skills/my-skill/
705
705
  # - OpenAI Codex CLI: .codex/skills/my-skill/
706
- # - OpenCode: .opencode/skill/my-skill/
706
+ # - OpenCode: .opencode/skills/my-skill/
707
707
  # - Pi Coding Agent: .pi/skills/my-skill/
708
708
  # - Goose & Amp: .agents/skills/my-skill/
709
709
  # - Antigravity: .agent/skills/my-skill/
package/dist/constants.js CHANGED
@@ -54,7 +54,7 @@ exports.SKILLS_DIR = 'skills';
54
54
  exports.RULER_SKILLS_PATH = '.ruler/skills';
55
55
  exports.CLAUDE_SKILLS_PATH = '.claude/skills';
56
56
  exports.CODEX_SKILLS_PATH = '.codex/skills';
57
- exports.OPENCODE_SKILLS_PATH = '.opencode/skill';
57
+ exports.OPENCODE_SKILLS_PATH = '.opencode/skills';
58
58
  exports.PI_SKILLS_PATH = '.pi/skills';
59
59
  exports.GOOSE_SKILLS_PATH = '.agents/skills';
60
60
  exports.VIBE_SKILLS_PATH = '.vibe/skills';
@@ -72,7 +72,7 @@ async function discoverSkills(projectRoot) {
72
72
  * Gets the paths that skills will generate, for gitignore purposes.
73
73
  * Returns empty array if skills directory doesn't exist.
74
74
  */
75
- async function getSkillsGitignorePaths(projectRoot) {
75
+ async function getSkillsGitignorePaths(projectRoot, agents) {
76
76
  const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
77
77
  // Check if skills directory exists
78
78
  try {
@@ -83,19 +83,21 @@ async function getSkillsGitignorePaths(projectRoot) {
83
83
  }
84
84
  // Import here to avoid circular dependency
85
85
  const { CLAUDE_SKILLS_PATH, CODEX_SKILLS_PATH, OPENCODE_SKILLS_PATH, PI_SKILLS_PATH, GOOSE_SKILLS_PATH, VIBE_SKILLS_PATH, ROO_SKILLS_PATH, GEMINI_SKILLS_PATH, CURSOR_SKILLS_PATH, FACTORY_SKILLS_PATH, ANTIGRAVITY_SKILLS_PATH, } = await Promise.resolve().then(() => __importStar(require('../constants')));
86
- return [
87
- path.join(projectRoot, CLAUDE_SKILLS_PATH),
88
- path.join(projectRoot, CODEX_SKILLS_PATH),
89
- path.join(projectRoot, OPENCODE_SKILLS_PATH),
90
- path.join(projectRoot, PI_SKILLS_PATH),
91
- path.join(projectRoot, GOOSE_SKILLS_PATH),
92
- path.join(projectRoot, VIBE_SKILLS_PATH),
93
- path.join(projectRoot, ROO_SKILLS_PATH),
94
- path.join(projectRoot, GEMINI_SKILLS_PATH),
95
- path.join(projectRoot, CURSOR_SKILLS_PATH),
96
- path.join(projectRoot, FACTORY_SKILLS_PATH),
97
- path.join(projectRoot, ANTIGRAVITY_SKILLS_PATH),
98
- ];
86
+ const selectedTargets = getSelectedSkillTargets(agents);
87
+ const targetPaths = {
88
+ claude: CLAUDE_SKILLS_PATH,
89
+ codex: CODEX_SKILLS_PATH,
90
+ opencode: OPENCODE_SKILLS_PATH,
91
+ pi: PI_SKILLS_PATH,
92
+ goose: GOOSE_SKILLS_PATH,
93
+ vibe: VIBE_SKILLS_PATH,
94
+ roo: ROO_SKILLS_PATH,
95
+ gemini: GEMINI_SKILLS_PATH,
96
+ cursor: CURSOR_SKILLS_PATH,
97
+ factory: FACTORY_SKILLS_PATH,
98
+ antigravity: ANTIGRAVITY_SKILLS_PATH,
99
+ };
100
+ return Array.from(selectedTargets).map((target) => path.join(projectRoot, targetPaths[target]));
99
101
  }
100
102
  /**
101
103
  * Module-level state to track if experimental warning has been shown.
@@ -115,6 +117,31 @@ function warnOnceExperimental(verbose, dryRun) {
115
117
  hasWarnedExperimental = true;
116
118
  (0, constants_1.logWarn)('Skills support is experimental and behavior may change in future releases.', dryRun);
117
119
  }
120
+ const SKILL_TARGET_TO_IDENTIFIERS = new Map([
121
+ ['claude', ['claude', 'copilot', 'kilocode']],
122
+ ['codex', ['codex']],
123
+ ['opencode', ['opencode']],
124
+ ['pi', ['pi']],
125
+ ['goose', ['goose', 'amp']],
126
+ ['vibe', ['mistral']],
127
+ ['roo', ['roo']],
128
+ ['gemini', ['gemini-cli']],
129
+ ['cursor', ['cursor']],
130
+ ['factory', ['factory']],
131
+ ['antigravity', ['antigravity']],
132
+ ]);
133
+ function getSelectedSkillTargets(agents) {
134
+ const selectedIdentifiers = new Set(agents
135
+ .filter((agent) => agent.supportsNativeSkills?.())
136
+ .map((agent) => agent.getIdentifier()));
137
+ const targets = new Set();
138
+ for (const [target, identifiers] of SKILL_TARGET_TO_IDENTIFIERS) {
139
+ if (identifiers.some((id) => selectedIdentifiers.has(id))) {
140
+ targets.add(target);
141
+ }
142
+ }
143
+ return targets;
144
+ }
118
145
  /**
119
146
  * Cleans up skills directories when skills are disabled.
120
147
  * This ensures that stale skills from previous runs don't persist when skills are turned off.
@@ -159,7 +186,7 @@ async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
159
186
  catch {
160
187
  // Directory doesn't exist, nothing to clean
161
188
  }
162
- // Clean up .opencode/skill
189
+ // Clean up .opencode/skills
163
190
  try {
164
191
  await fs.access(opencodeSkillsPath);
165
192
  if (dryRun) {
@@ -330,28 +357,53 @@ async function propagateSkills(projectRoot, agents, skillsEnabled, verbose, dryR
330
357
  }
331
358
  // Warn about experimental features
332
359
  warnOnceExperimental(verbose, dryRun);
360
+ const selectedTargets = getSelectedSkillTargets(agents);
361
+ if (selectedTargets.size === 0) {
362
+ (0, constants_1.logVerboseInfo)('No selected agents require skills propagation, skipping skills propagation', verbose, dryRun);
363
+ return;
364
+ }
333
365
  // Copy to Claude skills directory if needed
334
- if (hasNativeSkillsAgent) {
335
- (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CLAUDE_SKILLS_PATH} for Claude Code, GitHub Copilot, and Kilo Code`, verbose, dryRun);
366
+ if (selectedTargets.has('claude')) {
367
+ (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CLAUDE_SKILLS_PATH} for Claude Code, GitHub Copilot, and KiloCode`, verbose, dryRun);
336
368
  await propagateSkillsForClaude(projectRoot, { dryRun });
369
+ }
370
+ if (selectedTargets.has('codex')) {
337
371
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CODEX_SKILLS_PATH} for OpenAI Codex CLI`, verbose, dryRun);
338
372
  await propagateSkillsForCodex(projectRoot, { dryRun });
373
+ }
374
+ if (selectedTargets.has('opencode')) {
339
375
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.OPENCODE_SKILLS_PATH} for OpenCode`, verbose, dryRun);
340
376
  await propagateSkillsForOpenCode(projectRoot, { dryRun });
377
+ }
378
+ if (selectedTargets.has('pi')) {
341
379
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.PI_SKILLS_PATH} for Pi Coding Agent`, verbose, dryRun);
342
380
  await propagateSkillsForPi(projectRoot, { dryRun });
381
+ }
382
+ if (selectedTargets.has('goose')) {
343
383
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.GOOSE_SKILLS_PATH} for Goose and Amp`, verbose, dryRun);
344
384
  await propagateSkillsForGoose(projectRoot, { dryRun });
385
+ }
386
+ if (selectedTargets.has('vibe')) {
345
387
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.VIBE_SKILLS_PATH} for Mistral Vibe`, verbose, dryRun);
346
388
  await propagateSkillsForVibe(projectRoot, { dryRun });
389
+ }
390
+ if (selectedTargets.has('roo')) {
347
391
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.ROO_SKILLS_PATH} for Roo Code`, verbose, dryRun);
348
392
  await propagateSkillsForRoo(projectRoot, { dryRun });
393
+ }
394
+ if (selectedTargets.has('gemini')) {
349
395
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.GEMINI_SKILLS_PATH} for Gemini CLI`, verbose, dryRun);
350
396
  await propagateSkillsForGemini(projectRoot, { dryRun });
397
+ }
398
+ if (selectedTargets.has('cursor')) {
351
399
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CURSOR_SKILLS_PATH} for Cursor`, verbose, dryRun);
352
400
  await propagateSkillsForCursor(projectRoot, { dryRun });
401
+ }
402
+ if (selectedTargets.has('factory')) {
353
403
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.FACTORY_SKILLS_PATH} for Factory Droid`, verbose, dryRun);
354
404
  await propagateSkillsForFactory(projectRoot, { dryRun });
405
+ }
406
+ if (selectedTargets.has('antigravity')) {
355
407
  (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.ANTIGRAVITY_SKILLS_PATH} for Antigravity`, verbose, dryRun);
356
408
  await propagateSkillsForAntigravity(projectRoot, { dryRun });
357
409
  }
@@ -458,7 +510,7 @@ async function propagateSkillsForCodex(projectRoot, options) {
458
510
  return [];
459
511
  }
460
512
  /**
461
- * Propagates skills for OpenCode by copying .ruler/skills to .opencode/skill.
513
+ * Propagates skills for OpenCode by copying .ruler/skills to .opencode/skills.
462
514
  * Uses atomic replace to ensure safe overwriting of existing skills.
463
515
  * Returns dry-run steps if dryRun is true, otherwise returns empty array.
464
516
  */
package/dist/lib.js CHANGED
@@ -125,7 +125,7 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
125
125
  if (skillsEnabledForGitignore) {
126
126
  // Skills enabled by default or explicitly
127
127
  const { getSkillsGitignorePaths } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
128
- const skillsPaths = await getSkillsGitignorePaths(projectRoot);
128
+ const skillsPaths = await getSkillsGitignorePaths(projectRoot, selectedAgents);
129
129
  allGeneratedPaths = [...generatedPaths, ...skillsPaths];
130
130
  }
131
131
  await (0, apply_engine_1.updateGitignore)(projectRoot, allGeneratedPaths, loadedConfig, cliGitignoreEnabled, dryRun);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectronica/ruler",
3
- "version": "0.3.32",
3
+ "version": "0.3.34",
4
4
  "description": "Ruler — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {