@intellectronica/ruler 0.3.10 → 0.3.11

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
@@ -54,16 +54,16 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
54
54
 
55
55
  ## Supported AI Agents
56
56
 
57
- | Agent | Rules File(s) | MCP Configuration / Notes |
58
- | ---------------- | ------------------------------------------------ | ------------------------------------------------ |
59
- | AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) |
60
- | GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` |
61
- | Claude Code | `CLAUDE.md` | `.mcp.json` |
62
- | OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` |
63
- | Jules | `AGENTS.md` | - |
64
- | Cursor | `.cursor/rules/ruler_cursor_instructions.mdc` | `.cursor/mcp.json` |
65
- | Windsurf | `.windsurf/rules/ruler_windsurf_instructions.md` | - |
66
- | Cline | `.clinerules` | - |
57
+ | Agent | Rules File(s) | MCP Configuration / Notes |
58
+ | ---------------- | --------------------------------------------- | ------------------------------------------------ |
59
+ | AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) |
60
+ | GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` |
61
+ | Claude Code | `CLAUDE.md` | `.mcp.json` |
62
+ | OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` |
63
+ | Jules | `AGENTS.md` | - |
64
+ | Cursor | `.cursor/rules/ruler_cursor_instructions.mdc` | `.cursor/mcp.json` |
65
+ | Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` |
66
+ | Cline | `.clinerules` | - |
67
67
  | Crush | `CRUSH.md` | `.crush.json` |
68
68
  | Amp | `AGENTS.md` | - |
69
69
  | Amazon Q CLI | `.amazonq/rules/ruler_q_rules.md` | `.amazonq/mcp.json` |
@@ -105,10 +105,11 @@ npx @intellectronica/ruler apply
105
105
  1. Navigate to your project's root directory
106
106
  2. Run `ruler init`
107
107
  3. This creates:
108
- - `.ruler/` directory
109
- - `.ruler/AGENTS.md`: The primary starter Markdown file for your rules
110
- - `.ruler/ruler.toml`: The main configuration file for Ruler (now contains sample MCP server sections; legacy `.ruler/mcp.json` no longer scaffolded)
111
- - (Optional legacy fallback) If you previously used `.ruler/instructions.md`, it is still respected when `AGENTS.md` is absent. (The prior runtime warning was removed.)
108
+
109
+ - `.ruler/` directory
110
+ - `.ruler/AGENTS.md`: The primary starter Markdown file for your rules
111
+ - `.ruler/ruler.toml`: The main configuration file for Ruler (now contains sample MCP server sections; legacy `.ruler/mcp.json` no longer scaffolded)
112
+ - (Optional legacy fallback) If you previously used `.ruler/instructions.md`, it is still respected when `AGENTS.md` is absent. (The prior runtime warning was removed.)
112
113
 
113
114
  Additionally, you can create a global configuration to use when no local `.ruler/` directory is found:
114
115
 
@@ -160,7 +161,15 @@ project/
160
161
 
161
162
  - Discover all `.ruler/` directories in the project hierarchy
162
163
  - Load and concatenate rules from each directory in order
163
- - Enable with: `ruler apply --nested`
164
+ - Decide whether nested mode is enabled using the following precedence:
165
+ 1. `ruler apply --nested` (or `--no-nested`) takes top priority
166
+ 2. `nested = true` in `ruler.toml`
167
+ 3. Default to disabled when neither option is provided
168
+ - When a run is nested, downstream configs are forced to keep `nested = true`. If a child config attempts to disable it, Ruler keeps nested processing active and emits a warning in the logs.
169
+ - Nested processing carries forward each directory's own MCP bundle and configuration settings so that generated files remain scoped to their source directories while being normalized back to the project root.
170
+
171
+ > [!CAUTION]
172
+ > Nested mode is experimental and may change in future releases. The CLI logs this warning the first time a nested run is detected so you know the behavior may evolve.
164
173
 
165
174
  **Perfect for:**
166
175
 
@@ -212,21 +221,22 @@ The `apply` command looks for `.ruler/` in the current directory tree, reading t
212
221
 
213
222
  ### Options
214
223
 
215
- | Option | Description |
216
- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
217
- | `--project-root <path>` | Path to your project's root (default: current directory) |
218
- | `--agents <agent1,agent2,...>` | Comma-separated list of agent names to target (agentsmd, aider, amazonqcli, amp, augmentcode, claude, cline, codex, copilot, crush, cursor, firebase, firebender, gemini-cli, goose, jules, junie, kilocode, kiro, opencode, openhands, qwen, roo, trae, warp, windsurf, zed) |
219
- | `--config <path>` | Path to a custom `ruler.toml` configuration file |
220
- | `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true) |
221
- | `--no-mcp` | Disable applying MCP server configurations |
222
- | `--mcp-overwrite` | Overwrite native MCP config entirely instead of merging |
223
- | `--gitignore` | Enable automatic .gitignore updates (default: true) |
224
- | `--no-gitignore` | Disable automatic .gitignore updates |
225
- | `--nested` | Enable nested rule loading from nested .ruler directories (default: disabled) |
226
- | `--backup` | Enable/disable creation of .bak backup files (default: enabled) |
227
- | `--dry-run` | Preview changes without writing files |
228
- | `--local-only` | Do not look for configuration in `$XDG_CONFIG_HOME` |
229
- | `--verbose` / `-v` | Display detailed output during execution |
224
+ | Option | Description |
225
+ | ------------------------------ | ---------------------------------------------------------------------- |
226
+ | `--project-root <path>` | Project root path (default: current directory). |
227
+ | `--agents <agent1,agent2,...>` | Comma-separated agent names to target (see supported list below). |
228
+ | `--config <path>` | Custom `ruler.toml` path. |
229
+ | `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true). |
230
+ | `--no-mcp` | Disable applying MCP server configurations. |
231
+ | `--mcp-overwrite` | Overwrite native MCP config instead of merging. |
232
+ | `--gitignore` | Enable automatic .gitignore updates (default: true). |
233
+ | `--no-gitignore` | Disable automatic .gitignore updates. |
234
+ | `--nested` | Enable nested rule loading (default: inherit from config or disabled). |
235
+ | `--no-nested` | Disable nested rule loading even if `nested = true` in config. |
236
+ | `--backup` | Toggle creation of `.bak` backup files (default: enabled). |
237
+ | `--dry-run` | Preview changes without writing files. |
238
+ | `--local-only` | Skip `$XDG_CONFIG_HOME` when looking for configuration. |
239
+ | `--verbose` / `-v` | Display detailed output during execution. |
230
240
 
231
241
  ### Common Examples
232
242
 
@@ -305,15 +315,15 @@ ruler revert [options]
305
315
 
306
316
  ### Options
307
317
 
308
- | Option | Description |
309
- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
310
- | `--project-root <path>` | Path to your project's root (default: current directory) |
318
+ | Option | Description |
319
+ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
320
+ | `--project-root <path>` | Path to your project's root (default: current directory) |
311
321
  | `--agents <agent1,agent2,...>` | Comma-separated list of agent names to revert (agentsmd, aider, amazonqcli, amp, augmentcode, claude, cline, codex, copilot, crush, cursor, firebase, firebender, gemini-cli, goose, jules, junie, kilocode, kiro, opencode, openhands, qwen, roo, trae, warp, windsurf, zed) |
312
- | `--config <path>` | Path to a custom `ruler.toml` configuration file |
313
- | `--keep-backups` | Keep backup files (.bak) after restoration (default: false) |
314
- | `--dry-run` | Preview changes without actually reverting files |
315
- | `--verbose` / `-v` | Display detailed output during execution |
316
- | `--local-only` | Only search for local .ruler directories, ignore global config |
322
+ | `--config <path>` | Path to a custom `ruler.toml` configuration file |
323
+ | `--keep-backups` | Keep backup files (.bak) after restoration (default: false) |
324
+ | `--dry-run` | Preview changes without actually reverting files |
325
+ | `--verbose` / `-v` | Display detailed output during execution |
326
+ | `--local-only` | Only search for local .ruler directories, ignore global config |
317
327
 
318
328
  ### Common Examples
319
329
 
@@ -525,6 +535,7 @@ DEBUG = "1"
525
535
  ```
526
536
 
527
537
  **Remote servers** require a `url` field (headers optional; bearer Authorization token auto-extracted for OpenHands when possible):
538
+
528
539
  ```toml
529
540
  [mcp_servers.remote_server]
530
541
  url = "https://api.example.com"
@@ -543,6 +554,135 @@ Ruler uses this configuration with the `merge` (default) or `overwrite` strategy
543
554
  export CODEX_HOME="$(pwd)/.codex"
544
555
  ```
545
556
 
557
+ ## Skills Support (Experimental)
558
+
559
+ **⚠️ Experimental Feature**: Skills support is currently experimental and requires `uv` (the Python package manager) to be installed on your system for MCP-based agent integration.
560
+
561
+ Ruler can manage and propagate Claude Code-compatible skills to supported AI agents. Skills are stored in `.ruler/skills/` and are automatically distributed to compatible agents when you run `ruler apply`.
562
+
563
+ ### How It Works
564
+
565
+ Skills are specialized knowledge packages that extend AI agent capabilities with domain-specific expertise, workflows, or tool integrations. Ruler discovers skills in your `.ruler/skills/` directory and propagates them to compatible agents:
566
+
567
+ - **Claude Code agents**: Skills are copied to `.claude/skills/` in their native format
568
+ - **Other MCP-compatible agents**: Skills are copied to `.skillz/` and a Skillz MCP server is automatically configured via `uvx`
569
+
570
+ ### Skills Directory Structure
571
+
572
+ Skills can be organized flat or nested:
573
+
574
+ ```
575
+ .ruler/skills/
576
+ ├── my-skill/
577
+ │ ├── SKILL.md # Required: skill instructions/knowledge
578
+ │ ├── helper.py # Optional: additional resources (scripts)
579
+ │ └── reference.md # Optional: additional resources (docs)
580
+ └── another-skill/
581
+ └── SKILL.md
582
+ ```
583
+
584
+ Each skill must contain:
585
+ - `SKILL.md` - Primary skill file with instructions or knowledge base
586
+
587
+ Skills can optionally include additional resources like:
588
+ - Markdown files with supplementary documentation
589
+ - Python, JavaScript, or other scripts
590
+ - Configuration files or data
591
+
592
+ ### Configuration
593
+
594
+ Skills support is **enabled by default** but can be controlled via:
595
+
596
+ **CLI flags:**
597
+ ```bash
598
+ # Enable skills (default)
599
+ ruler apply --skills
600
+
601
+ # Disable skills
602
+ ruler apply --no-skills
603
+ ```
604
+
605
+ **Configuration in `ruler.toml`:**
606
+ ```toml
607
+ [skills]
608
+ enabled = true # or false to disable
609
+ ```
610
+
611
+ ### Skillz MCP Server
612
+
613
+ For agents that support MCP but don't have native skills support (all agents except Claude Code), Ruler automatically:
614
+
615
+ 1. Copies skills to `.skillz/` directory
616
+ 2. Configures a Skillz MCP server in the agent's configuration
617
+ 3. Uses `uvx` to launch the server with the absolute path to `.skillz`
618
+
619
+ Example auto-generated MCP server configuration:
620
+ ```toml
621
+ [mcp_servers.skillz]
622
+ command = "uvx"
623
+ args = ["skillz@latest", "/absolute/path/to/project/.skillz"]
624
+ ```
625
+
626
+ ### `.gitignore` Integration
627
+
628
+ When skills support is enabled and gitignore integration is active, Ruler automatically adds:
629
+ - `.claude/skills/` (for Claude Code agents)
630
+ - `.skillz/` (for MCP-based agents)
631
+
632
+ to your `.gitignore` file within the managed Ruler block.
633
+
634
+ ### Requirements
635
+
636
+ - **For Claude Code**: No additional requirements
637
+ - **For MCP agents**: `uv` must be installed and available in your PATH
638
+ ```bash
639
+ # Install uv if needed
640
+ curl -LsSf https://astral.sh/uv/install.sh | sh
641
+ ```
642
+
643
+ ### Validation
644
+
645
+ Ruler validates discovered skills and issues warnings for:
646
+ - Missing required file (`SKILL.md`)
647
+ - Invalid directory structures (directories without `SKILL.md` and no sub-skills)
648
+
649
+ Warnings don't prevent propagation but help identify potential issues.
650
+
651
+ ### Dry-Run Mode
652
+
653
+ Test skills propagation without making changes:
654
+ ```bash
655
+ ruler apply --dry-run
656
+ ```
657
+
658
+ This shows which skills would be copied and which MCP servers would be configured.
659
+
660
+ ### Example Workflow
661
+
662
+ ```bash
663
+ # 1. Add a skill to your project
664
+ mkdir -p .ruler/skills/my-skill
665
+ cat > .ruler/skills/my-skill/SKILL.md << 'EOF'
666
+ # My Custom Skill
667
+
668
+ This skill provides specialized knowledge for...
669
+
670
+ ## Usage
671
+
672
+ When working on this project, always follow these guidelines:
673
+ - Use TypeScript for all new code
674
+ - Write tests for all features
675
+ - Follow the existing code style
676
+ EOF
677
+
678
+ # 2. Apply to all agents (skills enabled by default)
679
+ ruler apply
680
+
681
+ # 3. Skills are now available to compatible agents:
682
+ # - Claude Code: .claude/skills/my-skill/
683
+ # - Other MCP agents: .skillz/my-skill/ + Skillz MCP server configured
684
+ ```
685
+
546
686
  ## `.gitignore` Integration
547
687
 
548
688
  Ruler automatically manages your `.gitignore` file to keep generated agent configuration files out of version control.
@@ -599,7 +739,7 @@ ruler apply
599
739
 
600
740
  ### Scenario 2: Complex Projects with Nested Rules
601
741
 
602
- For large projects with multiple components or services, use nested rule loading:
742
+ For large projects with multiple components or services, enable nested rule loading so each directory keeps its own rules and MCP bundle:
603
743
 
604
744
  ```bash
605
745
  # Set up nested .ruler directories
@@ -609,12 +749,25 @@ mkdir -p src/.ruler tests/.ruler docs/.ruler
609
749
  echo "# API Design Guidelines" > src/.ruler/api_rules.md
610
750
  echo "# Testing Best Practices" > tests/.ruler/test_rules.md
611
751
  echo "# Documentation Standards" > docs/.ruler/docs_rules.md
752
+ ```
612
753
 
613
- # Apply with nested loading
614
- ruler apply --nested --verbose
754
+ ```toml
755
+ # .ruler/ruler.toml
756
+ nested = true
615
757
  ```
616
758
 
617
- This creates context-specific instructions for different parts of your project while maintaining global rules in the root `.ruler/` directory.
759
+ ```bash
760
+ # The CLI inherits nested mode from ruler.toml
761
+ ruler apply --verbose
762
+
763
+ # Override from the CLI at any time
764
+ ruler apply --no-nested
765
+ ```
766
+
767
+ This creates context-specific instructions for different parts of your project while maintaining global rules in the root `.ruler/` directory. Nested runs automatically keep every nested config enabled even if a child tries to disable it.
768
+
769
+ > [!NOTE]
770
+ > The CLI prints "Nested mode is experimental and may change in future releases." the first time nested processing runs. Expect refinements in future versions.
618
771
 
619
772
  ### Scenario 3: Team Standardization
620
773
 
@@ -719,7 +872,7 @@ This shows:
719
872
  A: Currently, all agents receive the same concatenated rules. For agent-specific instructions, include sections in your rule files like "## GitHub Copilot Specific" or "## Aider Configuration".
720
873
 
721
874
  **Q: How do I set up different instructions for different parts of my project?**
722
- A: Use the `--nested` flag with `ruler apply --nested`. This enables Ruler to discover and load rules from multiple `.ruler/` directories throughout your project hierarchy. Place component-specific instructions in `src/.ruler/`, test-specific rules in `tests/.ruler/`, etc., while keeping global rules in the root `.ruler/` directory.
875
+ A: Enable nested mode either by setting `nested = true` in `ruler.toml` or by passing `ruler apply --nested`. The CLI inherits the config setting by default, but `--no-nested` always wins if you need to opt out for a run. Nested mode keeps loading rules (and MCP settings) from every `.ruler/` directory in the hierarchy, forces child configs to remain nested, and logs "Nested mode is experimental and may change in future releases." if any nested processing occurs.
723
876
 
724
877
  **Q: How do I temporarily disable Ruler for an agent?**
725
878
  A: Set `enabled = false` in `ruler.toml` under `[agents.agentname]`, or use `--agents` flag to specify only the agents you want.
@@ -80,5 +80,12 @@ class AbstractAgent {
80
80
  supportsMcpRemote() {
81
81
  return false;
82
82
  }
83
+ /**
84
+ * Returns whether this agent has native skills support.
85
+ * Defaults to false if not overridden.
86
+ */
87
+ supportsNativeSkills() {
88
+ return false;
89
+ }
83
90
  }
84
91
  exports.AbstractAgent = AbstractAgent;
@@ -55,5 +55,8 @@ class ClaudeAgent extends AbstractAgent_1.AbstractAgent {
55
55
  supportsMcpRemote() {
56
56
  return true;
57
57
  }
58
+ supportsNativeSkills() {
59
+ return true;
60
+ }
58
61
  }
59
62
  exports.ClaudeAgent = ClaudeAgent;
@@ -1,88 +1,21 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.WindsurfAgent = void 0;
37
- const path = __importStar(require("path"));
38
- const AbstractAgent_1 = require("./AbstractAgent");
39
- const FileSystemUtils_1 = require("../core/FileSystemUtils");
4
+ const AgentsMdAgent_1 = require("./AgentsMdAgent");
40
5
  /**
41
6
  * Windsurf agent adapter.
7
+ * Now uses AGENTS.md format like other agents.
42
8
  */
43
- class WindsurfAgent extends AbstractAgent_1.AbstractAgent {
9
+ class WindsurfAgent extends AgentsMdAgent_1.AgentsMdAgent {
44
10
  getIdentifier() {
45
11
  return 'windsurf';
46
12
  }
47
13
  getName() {
48
14
  return 'Windsurf';
49
15
  }
50
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
51
- agentConfig, backup = true) {
52
- const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
53
- const absolutePath = path.resolve(projectRoot, output);
54
- // Windsurf expects a YAML front-matter block with a `trigger` flag.
55
- const frontMatter = ['---', 'trigger: always_on', '---', ''].join('\n');
56
- const content = `${frontMatter}${concatenatedRules.trimStart()}`;
57
- const maxFileSize = 10000; // 10K characters
58
- await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(absolutePath));
59
- if (backup) {
60
- await (0, FileSystemUtils_1.backupFile)(absolutePath);
61
- }
62
- // Check if content exceeds the 10K limit
63
- if (content.length <= maxFileSize) {
64
- // Content fits in single file - use original behavior
65
- await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, content);
66
- }
67
- else {
68
- // Content exceeds limit - split into multiple files
69
- console.warn(`[ruler] Warning: Windsurf rule content exceeds ${maxFileSize} characters (${content.length}). Splitting into multiple files.`);
70
- const files = this.splitContentIntoFiles(concatenatedRules.trimStart(), frontMatter, maxFileSize);
71
- // Write each split file
72
- const rulesDir = path.dirname(absolutePath);
73
- const baseName = path.basename(absolutePath, '.md');
74
- for (let i = 0; i < files.length; i++) {
75
- const fileName = `${baseName}_${i.toString().padStart(2, '0')}.md`;
76
- const filePath = path.join(rulesDir, fileName);
77
- if (backup) {
78
- await (0, FileSystemUtils_1.backupFile)(filePath);
79
- }
80
- await (0, FileSystemUtils_1.writeGeneratedFile)(filePath, files[i]);
81
- }
82
- }
83
- }
84
- getDefaultOutputPath(projectRoot) {
85
- return path.join(projectRoot, '.windsurf', 'rules', 'ruler_windsurf_instructions.md');
16
+ // Windsurf supports MCP configuration
17
+ getMcpServerKey() {
18
+ return 'mcpServers';
86
19
  }
87
20
  supportsMcpStdio() {
88
21
  return true;
@@ -90,69 +23,5 @@ class WindsurfAgent extends AbstractAgent_1.AbstractAgent {
90
23
  supportsMcpRemote() {
91
24
  return true;
92
25
  }
93
- /**
94
- * Gets all actual output paths that will be created, including split files.
95
- * This allows the gitignore system to know about split files before they're created.
96
- */
97
- getActualOutputPaths(concatenatedRules, projectRoot, agentConfig) {
98
- const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
99
- const absolutePath = path.resolve(projectRoot, output);
100
- // Windsurf expects a YAML front-matter block with a `trigger` flag.
101
- const frontMatter = ['---', 'trigger: always_on', '---', ''].join('\n');
102
- const content = `${frontMatter}${concatenatedRules.trimStart()}`;
103
- const maxFileSize = 10000; // 10K characters
104
- // Check if content will be split
105
- if (content.length <= maxFileSize) {
106
- // Content fits in single file
107
- return [absolutePath];
108
- }
109
- else {
110
- // Content will be split - calculate how many files will be created
111
- const files = this.splitContentIntoFiles(concatenatedRules.trimStart(), frontMatter, maxFileSize);
112
- const rulesDir = path.dirname(absolutePath);
113
- const baseName = path.basename(absolutePath, '.md');
114
- const splitPaths = [];
115
- for (let i = 0; i < files.length; i++) {
116
- const fileName = `${baseName}_${i.toString().padStart(2, '0')}.md`;
117
- const filePath = path.join(rulesDir, fileName);
118
- splitPaths.push(filePath);
119
- }
120
- return splitPaths;
121
- }
122
- }
123
- /**
124
- * Splits content into multiple files, each under the specified size limit.
125
- * Splits at the closest newline within the limit.
126
- * Each file gets its own front-matter.
127
- */
128
- splitContentIntoFiles(rules, frontMatter, maxFileSize) {
129
- const files = [];
130
- const availableSpace = maxFileSize - frontMatter.length;
131
- let remainingRules = rules;
132
- while (remainingRules.length > 0) {
133
- if (remainingRules.length <= availableSpace) {
134
- // Remaining content fits in one file
135
- files.push(`${frontMatter}${remainingRules}`);
136
- break;
137
- }
138
- // Find the last newline within the available space
139
- let splitIndex = availableSpace;
140
- const searchSpace = remainingRules.substring(0, availableSpace);
141
- const lastNewline = searchSpace.lastIndexOf('\n');
142
- if (lastNewline > 0) {
143
- // Split at the newline (include the newline in the current file)
144
- splitIndex = lastNewline + 1;
145
- }
146
- else {
147
- // No newline found within limit - split at the limit
148
- // This shouldn't happen often but we handle it gracefully
149
- splitIndex = availableSpace;
150
- }
151
- const chunk = remainingRules.substring(0, splitIndex);
152
- files.push(`${frontMatter}${chunk}`);
153
- remainingRules = remainingRules.substring(splitIndex);
154
- }
155
- return files;
156
- }
157
26
  }
158
27
  exports.WindsurfAgent = WindsurfAgent;
@@ -63,13 +63,16 @@ function run() {
63
63
  })
64
64
  .option('nested', {
65
65
  type: 'boolean',
66
- description: 'Enable nested rule loading from nested .ruler directories (default: disabled)',
67
- default: false,
66
+ description: 'Enable nested rule loading from nested .ruler directories (default: from config or disabled)',
68
67
  })
69
68
  .option('backup', {
70
69
  type: 'boolean',
71
70
  description: 'Enable/disable creation of .bak backup files (default: enabled)',
72
71
  default: true,
72
+ })
73
+ .option('skills', {
74
+ type: 'boolean',
75
+ description: 'Enable/disable skills support (experimental, default: enabled)',
73
76
  });
74
77
  }, handlers_1.applyHandler)
75
78
  .command('init', 'Scaffold a .ruler directory with default files', (y) => {
@@ -42,6 +42,7 @@ const path = __importStar(require("path"));
42
42
  const os = __importStar(require("os"));
43
43
  const fs = __importStar(require("fs/promises"));
44
44
  const constants_1 = require("../constants");
45
+ const ConfigLoader_1 = require("../core/ConfigLoader");
45
46
  /**
46
47
  * Handler for the 'apply' command.
47
48
  */
@@ -58,7 +59,6 @@ async function applyHandler(argv) {
58
59
  const verbose = argv.verbose;
59
60
  const dryRun = argv['dry-run'];
60
61
  const localOnly = argv['local-only'];
61
- const nested = argv.nested;
62
62
  const backup = argv.backup;
63
63
  // Determine gitignore preference: CLI > TOML > Default (enabled)
64
64
  // yargs handles --no-gitignore by setting gitignore to false
@@ -69,8 +69,37 @@ async function applyHandler(argv) {
69
69
  else {
70
70
  gitignorePreference = undefined; // Let TOML/default decide
71
71
  }
72
+ // Determine nested preference: CLI > TOML > Default (false)
73
+ let nested;
74
+ if (argv.nested !== undefined) {
75
+ // CLI explicitly set nested (either --nested or --no-nested)
76
+ nested = argv.nested;
77
+ }
78
+ else {
79
+ // CLI didn't set nested, check TOML configuration
80
+ try {
81
+ const config = await (0, ConfigLoader_1.loadConfig)({
82
+ projectRoot,
83
+ configPath,
84
+ });
85
+ // Use TOML setting if available, otherwise default to false
86
+ nested = config.nested ?? false;
87
+ }
88
+ catch {
89
+ // If config loading fails, use default (false)
90
+ nested = false;
91
+ }
92
+ }
93
+ // Determine skills preference: CLI > TOML > Default (enabled)
94
+ let skillsEnabled;
95
+ if (argv.skills !== undefined) {
96
+ skillsEnabled = argv.skills;
97
+ }
98
+ else {
99
+ skillsEnabled = undefined; // Let config/default decide
100
+ }
72
101
  try {
73
- await (0, lib_1.applyAllAgentConfigs)(projectRoot, agents, configPath, mcpEnabled, mcpStrategy, gitignorePreference, verbose, dryRun, localOnly, nested, backup);
102
+ await (0, lib_1.applyAllAgentConfigs)(projectRoot, agents, configPath, mcpEnabled, mcpStrategy, gitignorePreference, verbose, dryRun, localOnly, nested, backup, skillsEnabled);
74
103
  console.log('Ruler apply completed successfully.');
75
104
  }
76
105
  catch (err) {
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_RULES_FILENAME = exports.ERROR_PREFIX = void 0;
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;
4
4
  exports.actionPrefix = actionPrefix;
5
5
  exports.createRulerError = createRulerError;
6
6
  exports.logVerbose = logVerbose;
@@ -49,3 +49,10 @@ function logVerboseInfo(message, isVerbose, dryRun = false) {
49
49
  console.log(`${prefix} ${message}`);
50
50
  }
51
51
  }
52
+ // Skills-related constants
53
+ exports.SKILLS_DIR = 'skills';
54
+ exports.RULER_SKILLS_PATH = '.ruler/skills';
55
+ exports.CLAUDE_SKILLS_PATH = '.claude/skills';
56
+ exports.SKILLZ_DIR = '.skillz';
57
+ exports.SKILL_MD_FILENAME = 'SKILL.md';
58
+ exports.SKILLZ_MCP_SERVER_NAME = 'skillz';
@@ -69,6 +69,11 @@ const rulerConfigSchema = zod_1.z.object({
69
69
  enabled: zod_1.z.boolean().optional(),
70
70
  })
71
71
  .optional(),
72
+ skills: zod_1.z
73
+ .object({
74
+ enabled: zod_1.z.boolean().optional(),
75
+ })
76
+ .optional(),
72
77
  nested: zod_1.z.boolean().optional(),
73
78
  });
74
79
  /**
@@ -175,13 +180,23 @@ async function loadConfig(options) {
175
180
  if (typeof rawGitignoreSection.enabled === 'boolean') {
176
181
  gitignoreConfig.enabled = rawGitignoreSection.enabled;
177
182
  }
178
- const nested = typeof raw.nested === 'boolean' ? raw.nested : false;
183
+ const rawSkillsSection = raw.skills && typeof raw.skills === 'object' && !Array.isArray(raw.skills)
184
+ ? raw.skills
185
+ : {};
186
+ const skillsConfig = {};
187
+ if (typeof rawSkillsSection.enabled === 'boolean') {
188
+ skillsConfig.enabled = rawSkillsSection.enabled;
189
+ }
190
+ const nestedDefined = typeof raw.nested === 'boolean';
191
+ const nested = nestedDefined ? raw.nested : false;
179
192
  return {
180
193
  defaultAgents,
181
194
  agentConfigs,
182
195
  cliAgents,
183
196
  mcp: globalMcpConfig,
184
197
  gitignore: gitignoreConfig,
198
+ skills: skillsConfig,
185
199
  nested,
200
+ nestedDefined,
186
201
  };
187
202
  }