@intellectronica/ruler 0.3.17 → 0.3.18

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.
@@ -139,5 +139,8 @@ class CodexCliAgent {
139
139
  supportsMcpRemote() {
140
140
  return false; // Codex CLI only supports STDIO based on PR description
141
141
  }
142
+ supportsNativeSkills() {
143
+ return true;
144
+ }
142
145
  }
143
146
  exports.CodexCliAgent = CodexCliAgent;
@@ -39,5 +39,8 @@ class CopilotAgent {
39
39
  supportsMcpRemote() {
40
40
  return true;
41
41
  }
42
+ supportsNativeSkills() {
43
+ return true;
44
+ }
42
45
  }
43
46
  exports.CopilotAgent = CopilotAgent;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SKILLZ_MCP_SERVER_NAME = exports.SKILL_MD_FILENAME = exports.SKILLZ_DIR = exports.CLAUDE_SKILLS_PATH = exports.RULER_SKILLS_PATH = exports.SKILLS_DIR = exports.DEFAULT_RULES_FILENAME = exports.ERROR_PREFIX = void 0;
3
+ exports.SKILLZ_MCP_SERVER_NAME = exports.SKILL_MD_FILENAME = exports.SKILLZ_DIR = exports.CODEX_SKILLS_PATH = exports.CLAUDE_SKILLS_PATH = exports.RULER_SKILLS_PATH = exports.SKILLS_DIR = exports.DEFAULT_RULES_FILENAME = exports.ERROR_PREFIX = void 0;
4
4
  exports.actionPrefix = actionPrefix;
5
5
  exports.createRulerError = createRulerError;
6
6
  exports.logVerbose = logVerbose;
@@ -53,6 +53,7 @@ function logVerboseInfo(message, isVerbose, dryRun = false) {
53
53
  exports.SKILLS_DIR = 'skills';
54
54
  exports.RULER_SKILLS_PATH = '.ruler/skills';
55
55
  exports.CLAUDE_SKILLS_PATH = '.claude/skills';
56
+ exports.CODEX_SKILLS_PATH = '.codex/skills';
56
57
  exports.SKILLZ_DIR = '.skillz';
57
58
  exports.SKILL_MD_FILENAME = 'SKILL.md';
58
59
  exports.SKILLZ_MCP_SERVER_NAME = 'skillz';
@@ -37,6 +37,7 @@ exports.discoverSkills = discoverSkills;
37
37
  exports.getSkillsGitignorePaths = getSkillsGitignorePaths;
38
38
  exports.propagateSkills = propagateSkills;
39
39
  exports.propagateSkillsForClaude = propagateSkillsForClaude;
40
+ exports.propagateSkillsForCodex = propagateSkillsForCodex;
40
41
  exports.propagateSkillsForSkillz = propagateSkillsForSkillz;
41
42
  exports.buildSkillzMcpConfig = buildSkillzMcpConfig;
42
43
  const path = __importStar(require("path"));
@@ -74,9 +75,10 @@ async function getSkillsGitignorePaths(projectRoot) {
74
75
  return [];
75
76
  }
76
77
  // Import here to avoid circular dependency
77
- const { CLAUDE_SKILLS_PATH, SKILLZ_DIR } = await Promise.resolve().then(() => __importStar(require('../constants')));
78
+ const { CLAUDE_SKILLS_PATH, CODEX_SKILLS_PATH, SKILLZ_DIR } = await Promise.resolve().then(() => __importStar(require('../constants')));
78
79
  return [
79
80
  path.join(projectRoot, CLAUDE_SKILLS_PATH),
81
+ path.join(projectRoot, CODEX_SKILLS_PATH),
80
82
  path.join(projectRoot, SKILLZ_DIR),
81
83
  ];
82
84
  }
@@ -100,11 +102,12 @@ function warnOnceExperimentalAndUv(verbose, dryRun) {
100
102
  (0, constants_1.logWarn)('Skills MCP server (Skillz) requires uv. Install: https://github.com/astral-sh/uv', dryRun);
101
103
  }
102
104
  /**
103
- * Cleans up skills directories (.claude/skills and .skillz) when skills are disabled.
105
+ * Cleans up skills directories (.claude/skills, .codex/skills and .skillz) when skills are disabled.
104
106
  * This ensures that stale skills from previous runs don't persist when skills are turned off.
105
107
  */
106
108
  async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
107
109
  const claudeSkillsPath = path.join(projectRoot, constants_1.CLAUDE_SKILLS_PATH);
110
+ const codexSkillsPath = path.join(projectRoot, constants_1.CODEX_SKILLS_PATH);
108
111
  const skillzPath = path.join(projectRoot, constants_1.SKILLZ_DIR);
109
112
  // Clean up .claude/skills
110
113
  try {
@@ -120,6 +123,20 @@ async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
120
123
  catch {
121
124
  // Directory doesn't exist, nothing to clean
122
125
  }
126
+ // Clean up .codex/skills
127
+ try {
128
+ await fs.access(codexSkillsPath);
129
+ if (dryRun) {
130
+ (0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.CODEX_SKILLS_PATH}`, verbose, dryRun);
131
+ }
132
+ else {
133
+ await fs.rm(codexSkillsPath, { recursive: true, force: true });
134
+ (0, constants_1.logVerboseInfo)(`Removed ${constants_1.CODEX_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
135
+ }
136
+ }
137
+ catch {
138
+ // Directory doesn't exist, nothing to clean
139
+ }
123
140
  // Clean up .skillz
124
141
  try {
125
142
  await fs.access(skillzPath);
@@ -178,8 +195,10 @@ async function propagateSkills(projectRoot, agents, skillsEnabled, verbose, dryR
178
195
  }
179
196
  // Copy to Claude skills directory if needed
180
197
  if (hasNativeSkillsAgent) {
181
- (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CLAUDE_SKILLS_PATH} for Claude Code`, verbose, dryRun);
198
+ (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CLAUDE_SKILLS_PATH} for Claude Code and GitHub Copilot`, verbose, dryRun);
182
199
  await propagateSkillsForClaude(projectRoot, { dryRun });
200
+ (0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CODEX_SKILLS_PATH} for OpenAI Codex CLI`, verbose, dryRun);
201
+ await propagateSkillsForCodex(projectRoot, { dryRun });
183
202
  }
184
203
  // Copy to .skillz directory if needed
185
204
  if (hasMcpAgent) {
@@ -237,6 +256,56 @@ async function propagateSkillsForClaude(projectRoot, options) {
237
256
  }
238
257
  return [];
239
258
  }
259
+ /**
260
+ * Propagates skills for OpenAI Codex CLI by copying .ruler/skills to .codex/skills.
261
+ * Uses atomic replace to ensure safe overwriting of existing skills.
262
+ * Returns dry-run steps if dryRun is true, otherwise returns empty array.
263
+ */
264
+ async function propagateSkillsForCodex(projectRoot, options) {
265
+ const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
266
+ const codexSkillsPath = path.join(projectRoot, constants_1.CODEX_SKILLS_PATH);
267
+ const codexDir = path.dirname(codexSkillsPath);
268
+ // Check if source skills directory exists
269
+ try {
270
+ await fs.access(skillsDir);
271
+ }
272
+ catch {
273
+ // No skills directory - return empty
274
+ return [];
275
+ }
276
+ if (options.dryRun) {
277
+ return [`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.CODEX_SKILLS_PATH}`];
278
+ }
279
+ // Ensure .codex directory exists
280
+ await fs.mkdir(codexDir, { recursive: true });
281
+ // Use atomic replace: copy to temp, then rename
282
+ const tempDir = path.join(codexDir, `skills.tmp-${Date.now()}`);
283
+ try {
284
+ // Copy to temp directory
285
+ await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
286
+ // Atomically replace the target
287
+ // First, remove existing target if it exists
288
+ try {
289
+ await fs.rm(codexSkillsPath, { recursive: true, force: true });
290
+ }
291
+ catch {
292
+ // Target didn't exist, that's fine
293
+ }
294
+ // Rename temp to target
295
+ await fs.rename(tempDir, codexSkillsPath);
296
+ }
297
+ catch (error) {
298
+ // Clean up temp directory on error
299
+ try {
300
+ await fs.rm(tempDir, { recursive: true, force: true });
301
+ }
302
+ catch {
303
+ // Ignore cleanup errors
304
+ }
305
+ throw error;
306
+ }
307
+ return [];
308
+ }
240
309
  /**
241
310
  * Propagates skills for MCP agents by copying .ruler/skills to .skillz.
242
311
  * Uses atomic replace to ensure safe overwriting of existing skills.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectronica/ruler",
3
- "version": "0.3.17",
3
+ "version": "0.3.18",
4
4
  "description": "Ruler — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {