diff --git a/README.md b/README.md index 0b449d3..dc3a924 100644 --- a/README.md +++ b/README.md @@ -1,997 +1,102 @@ # `skiller` -A Claude-centric fork of [ruler](https://github.com/intellectronica/ruler) with native skills support: - -## 1. Skills as Source of Truth - -- `.claude/skills/` is the committed source of truth for skills -- **Bidirectional sync** between `.mdc` and `SKILL.md` (sibling pattern): - - Create `.claude/skills/foo/foo.mdc` → auto-generates `.claude/skills/foo/SKILL.md` - - Create `.claude/skills/foo/SKILL.md` → auto-generates `.claude/skills/foo/foo.mdc` -- Sync direction detected via `@reference` body pattern (SKILL.md with `@path` = .mdc is source) -- Edit either file, the other stays in sync on next `skiller apply` -- `.claude/rules/` contents are migrated to `.claude/skills/` and rules directory is deleted - -## 2. CLAUDE.md @filename References - -- Uses `@filename` syntax instead of merging content -- Claude Code auto-includes referenced files -- Reduces CLAUDE.md size and keeps sources separate -- Other agents still get merged content - -## 3. MDC File Support - -- Supports both `.md` and `.mdc` files (Nuxt Content, Vue) -- All patterns auto-expand: `"components"` → `"components/**/*.{md,mdc}"` - -## 4. Rules Filtering - -- `include`/`exclude` glob patterns in `[rules]` -- Directory names auto-expand to `directory/**/*.{md,mdc}` -- Organize by team/feature, exclude drafts/internal docs - -## 5. Claude Root Folder - -- Default directory is `.claude/` (no extra flags needed) -- Skills already in `.claude/skills` (no copying) -- Single directory for all Claude Code config - -## 6. Cursor-style Rules - -- `merge_strategy = "cursor"` parses `.mdc` frontmatter -- Only includes rules with `alwaysApply: true` -- Strips frontmatter, keeps body only - -## 7. Backup Control - -- `[backup].enabled = false` disables `.bak` files - -## 8. Multi-Agent Skills Propagation - -- `.claude/skills/` is the source of truth — skills are automatically copied to agent-specific directories on `skiller apply` -- Supported agent paths: `.codex/skills`, `.cursor/skills`, `.opencode/skill`, `.roo/skills`, `.gemini/skills`, `.agents/skills` -- Shared paths are deduplicated (Claude/Copilot/Kilo share `.claude/skills`, Goose/Amp share `.agents/skills`) -- Agent skills directories are auto-added to `.gitignore` (excluding `.claude/skills`) -- Validates skill structure — warns on missing `SKILL.md` -- Flattens nested skills into dash-separated names for agent skills dirs (e.g., `workflows/lfg` → `workflows-lfg`) - -## 9. Claude Code Plugins → Skills - -- Reads `.claude/settings.json` `enabledPlugins` -- Reads plugin content from `~/.claude/plugins/marketplaces` (never from `cache/`) -- Syncs enabled plugin `skills/` into agent skills directories on `skiller apply` (recursive, flattened names) -- Syncs enabled plugin `commands/**/*.md` as skills (`SKILL.md`) into agent skills directories (recursive, flattened names) -- Syncs enabled plugin `agents/**/*.md` as skills (`SKILL.md`) into agent skills directories -- Uses the skill/command/agent name by default (matches existing Codex skill names) -- If a name conflicts, local skills win and the plugin item is namespaced as `-` (numeric suffix if multiple enabled plugins share the same name) -- Tracks plugin-managed items in `.claude/.skiller.json` (per project, grouped by agent skills dir) -- Removes stale plugin skills when plugins are disabled - -## 10. Claude Commands/Agents → Skills - -- Syncs `.claude/commands/**/*.md` as skills (`SKILL.md`) into agent skills directories -- Flattens nested commands into dash-separated names (e.g., `workflows/brainstorm.md` → `workflows-brainstorm`) -- Syncs `.claude/agents/**/*.md` as skills (`SKILL.md`) into agent skills directories -- Uses the command/agent name by default -- If a name conflicts, existing local/manual skills win and the project item is namespaced as `claude-` -- Project items win over plugin skills/commands/agents on name conflicts -- Tracks project-managed items in `.claude/.skiller.json` (per project, grouped by agent skills dir) - ---- - -# Skiller: Centralise Your AI Coding Assistant Instructions - -> **Beta Research Preview** -> -> - Please test this version carefully in your environment -> - Report issues at https://github.com/udecode/skiller/issues - -## Why Skiller? - -Managing instructions across multiple AI coding tools becomes complex as your team grows. Different agents (GitHub Copilot, Claude, Cursor, Aider, etc.) require their own configuration files, leading to: - -- **Inconsistent guidance** across AI tools -- **Duplicated effort** maintaining multiple config files -- **Context drift** as project requirements evolve -- **Onboarding friction** for new AI tools -- **Complex project structures** requiring context-specific instructions for different components - -Skiller solves this by providing a **single source of truth** for all your AI agent instructions, automatically distributing them to the right configuration files. With support for **nested rule loading**, Skiller can handle complex project structures with context-specific instructions for different components. - -## Core Features - -- **Centralised Rule Management**: Store all AI instructions in a dedicated `.claude/` directory using Markdown files -- **Nested Rule Loading**: Support complex project structures with multiple `.claude/` directories for context-specific instructions -- **Automatic Distribution**: Skiller applies these rules to configuration files of supported AI agents -- **Targeted Agent Configuration**: Fine-tune which agents are affected and their specific output paths via `skiller.toml` -- **MCP Server Propagation**: Manage and distribute Model Context Protocol (MCP) server settings -- **`.gitignore` Automation**: Keeps generated agent config files out of version control automatically -- **Simple CLI**: Easy-to-use commands for initialising and applying configurations - -## Supported AI Agents - -| Agent | Rules File(s) | MCP Configuration / Notes | Skills Location | -| ---------------- | -------------------------------------------------- | ------------------------------------------------ | ------------------ | -| AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) | - | -| GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` | `.claude/skills/` | -| Claude Code | `CLAUDE.md` (@filename references) | `.mcp.json` | `.claude/skills/` | -| OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` | `.codex/skills/` | -| Jules | `AGENTS.md` | - | - | -| Cursor | `AGENTS.md` | `.cursor/mcp.json` | `.cursor/skills/` | -| Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` | - | -| Cline | `.clinerules` | - | - | -| Crush | `CRUSH.md` | `.crush.json` | - | -| Amp | `AGENTS.md` | - | `.agents/skills/` | -| Amazon Q CLI | `.amazonq/rules/skiller_q_rules.md` | `.amazonq/mcp.json` | - | -| Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` | - | -| Firebase Studio | `.idx/airules.md` | `.idx/mcp.json` | - | -| Open Hands | `.openhands/microagents/repo.md` | `config.toml` | - | -| Gemini CLI | `AGENTS.md` | `.gemini/settings.json` | `.gemini/skills/` | -| Junie | `.junie/guidelines.md` | - | - | -| AugmentCode | `.augment/rules/skiller_augment_instructions.md` | - | - | -| Kilo Code | `.kilocode/rules/skiller_kilocode_instructions.md` | `.kilocode/mcp.json` | `.claude/skills/` | -| OpenCode | `AGENTS.md` | `opencode.json` | `.opencode/skill/` | -| Goose | `.goosehints` | - | `.agents/skills/` | -| Qwen Code | `AGENTS.md` | `.qwen/settings.json` | - | -| RooCode | `AGENTS.md` | `.roo/mcp.json` | `.roo/skills/` | -| Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) | - | -| Trae AI | `.trae/rules/project_rules.md` | - | - | -| Warp | `WARP.md` | - | - | -| Kiro | `.kiro/steering/skiller_kiro_instructions.md` | - | - | -| Firebender | `firebender.json` | `firebender.json` (rules and MCP in same file) | - | - -## Getting Started - -### Installation - -**Using `npx` (for one-off commands):** +Apply the same rules (and skills) to multiple AI coding agents. ```bash +npx skiller@latest init npx skiller@latest apply ``` -### Project Initialisation - -1. Navigate to your project's root directory -2. Run `skiller init` -3. This creates: - -- `.claude/` directory -- `.claude/AGENTS.md`: The primary starter Markdown file for your rules -- `.claude/skiller.toml`: The main configuration file for Skiller - -Additionally, you can create a global configuration to use when no local `.claude/` directory is found: - -```bash -skiller init --global -``` - -The global configuration will be created to `$XDG_CONFIG_HOME/skiller` (default: `~/.config/skiller`). - -## Core Concepts - -### The `.claude/` Directory - -This is your central hub for all AI agent instructions: - -- **Primary File Order & Precedence**: - 1. A repository root `AGENTS.md` (outside `.claude/`) if present (highest precedence, prepended) - 2. `.claude/AGENTS.md` (new default starter file) - 3. Remaining discovered `.md` files under `.claude/` (and subdirectories) in sorted order -- **Rule Files (`*.md`)**: Discovered recursively from `.claude/` or `$XDG_CONFIG_HOME/skiller` and concatenated in the order above -- **Concatenation Marker**: Each file's content is prepended with `--- Source: ---` for traceability -- **`skiller.toml`**: Master configuration for Skiller's behavior, agent selection, and output paths -- **`mcp.json`**: Shared MCP server settings - -This ordering lets you keep a short, high-impact root `AGENTS.md` (e.g. executive project summary) while housing detailed guidance inside `.claude/`. - -### Nested Rule Loading - -Skiller now supports **nested rule loading** with the `--nested` flag, enabling context-specific instructions for different parts of your project: - -``` -project/ -├── .claude/ # Global project rules -│ ├── AGENTS.md -│ └── coding_style.md -├── src/ -│ └── .claude/ # Component-specific rules -│ └── api_guidelines.md -├── tests/ -│ └── .claude/ # Test-specific rules -│ └── testing_conventions.md -└── docs/ - └── .claude/ # Documentation rules - └── writing_style.md -``` - -**How it works:** - -- Discover all `.claude/` directories in the project hierarchy -- Load and concatenate rules from each directory in order -- Decide whether nested mode is enabled using the following precedence: - 1. `skiller apply --nested` (or `--no-nested`) takes top priority - 2. `nested = true` in `skiller.toml` - 3. Default to disabled when neither option is provided -- When a run is nested, downstream configs are forced to keep `nested = true`. If a child config attempts to disable it, Skiller keeps nested processing active and emits a warning in the logs. -- 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. - -> [!CAUTION] -> 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. - -**Perfect for:** - -- Monorepos with multiple services -- Projects with distinct components (frontend/backend) -- Teams needing different instructions for different areas -- Complex codebases with varying standards - -### Best Practices for Rule Files - -**Granularity**: Break down complex instructions into focused `.md` files: - -- `coding_style.md` -- `api_conventions.md` -- `project_architecture.md` -- `security_guidelines.md` - -**Example rule file (`.claude/python_guidelines.md`):** - -```markdown -# Python Project Guidelines - -## General Style - -- Follow PEP 8 for all Python code -- Use type hints for all function signatures and complex variables -- Keep functions short and focused on a single task - -## Error Handling - -- Use specific exception types rather than generic `Exception` -- Log errors effectively with context - -## Security - -- Always validate and sanitize user input -- Be mindful of potential injection vulnerabilities -``` - -## Usage: The `apply` Command - -### Primary Command - -```bash -skiller apply [options] -``` - -The `apply` command looks for `.claude/` in the current directory tree, reading the first match. If no such directory is found, it will look for a global configuration in `$XDG_CONFIG_HOME/skiller`. - -### Options - -| Option | Description | -| ------------------------------ | ---------------------------------------------------------------------- | -| `--project-root ` | Project root path (default: current directory). | -| `--agents ` | Comma-separated agent names to target (see supported list below). | -| `--config ` | Custom `skiller.toml` path. | -| `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true). | -| `--no-mcp` | Disable applying MCP server configurations. | -| `--mcp-overwrite` | Overwrite native MCP config instead of merging. | -| `--gitignore` | Enable automatic .gitignore updates (default: true). | -| `--no-gitignore` | Disable automatic .gitignore updates. | -| `--nested` | Enable nested rule loading (default: inherit from config or disabled). | -| `--no-nested` | Disable nested rule loading even if `nested = true` in config. | -| `--backup` / `--no-backup` | Enable/disable creation of `.bak` backup files (default: enabled). | -| `--dry-run` | Preview changes without writing files. | -| `--local-only` | Skip `$XDG_CONFIG_HOME` when looking for configuration. | -| `--verbose` / `-v` | Display detailed output during execution. | - -### Common Examples - -**Apply rules to all configured agents:** - -```bash -skiller apply -``` - -**Apply rules only to GitHub Copilot and Claude:** - -```bash -skiller apply --agents copilot,claude -``` - -**Apply rules only to Firebase Studio:** - -```bash -skiller apply --agents firebase -``` - -**Apply rules only to Warp:** - -```bash -skiller apply --agents warp -``` - -**Apply rules only to Trae AI:** - -```bash -skiller apply --agents trae -``` - -**Apply rules only to RooCode:** - -```bash -skiller apply --agents roo -``` - -**Use a specific configuration file:** - -```bash -skiller apply --config ./team-configs/skiller.frontend.toml -``` - -**Apply rules with verbose output:** - -```bash -skiller apply --verbose -``` - -**Apply rules but skip MCP and .gitignore updates:** - -```bash -skiller apply --no-mcp --no-gitignore -``` - -## Usage: The `revert` Command - -The `revert` command safely undoes all changes made by `skiller apply`, restoring your project to its pre-skiller state. It intelligently restores files from backups (`.bak` files) when available, or removes generated files that didn't exist before. - -### Why Revert is Needed - -When experimenting with different rule configurations or switching between projects, you may want to: - -- **Clean slate**: Remove all skiller-generated files to start fresh -- **Restore originals**: Revert modified files back to their original state -- **Selective cleanup**: Remove configurations for specific agents only -- **Safe experimentation**: Try skiller without fear of permanent changes - -### Primary Command - -```bash -skiller revert [options] -``` - -### Options - -| Option | Description | -| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--project-root ` | Path to your project's root (default: current directory) | -| `--agents ` | 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) | -| `--config ` | Path to a custom `skiller.toml` configuration file | -| `--keep-backups` | Keep backup files (.bak) after restoration (default: false) | -| `--dry-run` | Preview changes without actually reverting files | -| `--verbose` / `-v` | Display detailed output during execution | -| `--local-only` | Only search for local .claude directories, ignore global config | - -### Common Examples - -**Revert all skiller changes:** - -```bash -skiller revert -``` - -**Preview what would be reverted (dry-run):** - -```bash -skiller revert --dry-run -``` - -**Revert only specific agents:** - -```bash -skiller revert --agents claude,copilot -``` - -**Revert with detailed output:** - -```bash -skiller revert --verbose -``` - -**Keep backup files after reverting:** - -```bash -skiller revert --keep-backups -``` - -## Configuration (`skiller.toml`) in Detail - -### Location - -Defaults to `.claude/skiller.toml` in the project root. Override with `--config` CLI option. - -### Complete Example - -```toml -# Default agents to run when --agents is not specified -# Uses case-insensitive substring matching -default_agents = ["copilot", "claude", "aider"] - -# --- Global MCP Server Configuration --- -[mcp] -# Enable/disable MCP propagation globally (default: true) -enabled = true -# Global merge strategy: 'merge' or 'overwrite' (default: 'merge') -merge_strategy = "merge" - -# --- MCP Server Definitions --- -[mcp_servers.filesystem] -command = "npx" -args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"] - -[mcp_servers.git] -command = "npx" -args = ["-y", "@modelcontextprotocol/server-git", "--repository", "."] - -[mcp_servers.remote_api] -url = "https://api.example.com" - -[mcp_servers.remote_api.headers] -Authorization = "Bearer your-token" - -# --- Global .gitignore Configuration --- -[gitignore] -# Enable/disable automatic .gitignore updates (default: true) -enabled = true - -# --- Backup Configuration --- -[backup] -# Enable/disable creation of .bak backup files (default: true) -enabled = false - -# --- Agent-Specific Configurations --- -[agents.copilot] -enabled = true - -[agents.claude] -enabled = true -output_path = "CLAUDE.md" - -[agents.aider] -enabled = true -output_path_instructions = "AGENTS.md" -output_path_config = ".aider.conf.yml" - -# OpenAI Codex CLI agent and MCP config -[agents.codex] -enabled = true -output_path = "AGENTS.md" -output_path_config = ".codex/config.toml" - -# Agent-specific MCP configuration for Codex CLI -[agents.codex.mcp] -enabled = true -merge_strategy = "merge" - -[agents.firebase] -enabled = true -output_path = ".idx/airules.md" - -[agents.gemini-cli] -enabled = true - -[agents.jules] -enabled = true - -[agents.junie] -enabled = true -output_path = ".junie/guidelines.md" - -# Agent-specific MCP configuration -[agents.cursor.mcp] -enabled = true -merge_strategy = "merge" - -# Disable specific agents -[agents.windsurf] -enabled = false - -[agents.kilocode] -enabled = true -output_path = ".kilocode/rules/skiller_kilocode_instructions.md" - -[agents.warp] -enabled = true -output_path = "WARP.md" -``` - -### Configuration Precedence - -1. **CLI flags** (e.g., `--agents`, `--no-mcp`, `--mcp-overwrite`, `--no-gitignore`) -2. **Settings in `skiller.toml`** (`default_agents`, specific agent settings, global sections) -3. **Skiller's built-in defaults** (all agents enabled, standard output paths, MCP enabled with 'merge') - -## MCP (Model Context Protocol) Server Configuration - -MCP provides broader context to AI models through server configurations. Skiller can manage and distribute these settings across compatible agents. - -### TOML Configuration (Recommended) - -You can now define MCP servers directly in `skiller.toml` using the `[mcp_servers.]` syntax: - -```toml -# Global MCP behavior -[mcp] -enabled = true -merge_strategy = "merge" # or "overwrite" - -# Local (stdio) server -[mcp_servers.filesystem] -command = "npx" -args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"] - -[mcp_servers.filesystem.env] -API_KEY = "your-api-key" - -# Remote server -[mcp_servers.search] -url = "https://mcp.example.com" - -[mcp_servers.search.headers] -Authorization = "Bearer your-token" -"X-API-Version" = "v1" -``` - -### Configuration Precedence - -When both TOML and JSON configurations are present: - -1. **TOML servers take precedence** over JSON servers with the same name -2. **Servers are merged** from both sources (unless using overwrite strategy) -3. **Deprecation warning** is shown encouraging migration to TOML (warning shown once per run) - -### Server Types - -**Local/stdio servers** require a `command` field: - -```toml -[mcp_servers.local_server] -command = "node" -args = ["server.js"] - -[mcp_servers.local_server.env] -DEBUG = "1" -``` - -**Remote servers** require a `url` field (headers optional; bearer Authorization token auto-extracted for OpenHands when possible): - -```toml -[mcp_servers.remote_server] -url = "https://api.example.com" - -[mcp_servers.remote_server.headers] -Authorization = "Bearer token" -``` - -Skiller uses this configuration with the `merge` (default) or `overwrite` strategy, controlled by `skiller.toml` or CLI flags. - -**Home Directory Safety:** Skiller never writes MCP configuration files outside your project root. Any historical references to user home directories (e.g. `~/.codeium/windsurf/mcp_config.json` or `~/.zed/settings.json`) have been removed; only project-local paths are targeted. - -**Note for OpenAI Codex CLI:** To apply the local Codex CLI MCP configuration, set the `CODEX_HOME` environment variable to your project’s `.codex` directory: - -```bash -export CODEX_HOME="$(pwd)/.codex" -``` - -## Skills Support - -Skiller can manage and propagate skills to supported AI agents. Skills are stored in `.claude/skills/` as the **committed source of truth** and automatically copied to agent-specific directories on `skiller apply`. - -### How It Works - -Skills are specialized knowledge packages that extend AI agent capabilities. Skiller discovers skills in your `.claude/skills/` directory, keeps them in sync via bidirectional `.mdc`/`SKILL.md` sync, and propagates them to all agents with native skills support: - -- **Claude Code, GitHub Copilot, Kilo Code**: `.claude/skills/` (shared, source of truth) -- **OpenAI Codex CLI**: `.codex/skills/` -- **Cursor**: `.cursor/skills/` -- **OpenCode**: `.opencode/skill/` -- **Goose, Amp**: `.agents/skills/` (shared) -- **Roo Code**: `.roo/skills/` -- **Gemini CLI**: `.gemini/skills/` - -Shared paths are deduplicated — agents sharing the same directory only trigger one copy operation. - -### Claude Code Plugins - -If your project enables Claude Code plugins in `.claude/settings.json`, Skiller also syncs plugin content into agent skills directories on `skiller apply`: - -- Plugin `skills/` are copied as skills -- Plugin `commands/**/*.md` are converted into skills (`SKILL.md`) -- Plugin `agents/**/*.md` are converted into skills (`SKILL.md`) -- Plugin skills use their original skill/command name by default -- If a name conflicts, local skills win and the plugin item is namespaced as `-` (numeric suffix if multiple enabled plugins share the same name) -- Plugin-managed items are tracked via `.claude/.skiller.json` (per project, grouped by agent skills dir) - -### Skills Directory Structure - -Skills can be organized flat or nested: - -``` -.claude/skills/ -├── my-skill/ -│ ├── my-skill.mdc # Skill content (body) -│ └── SKILL.md # @reference to my-skill.mdc (frontmatter here) -├── another-skill/ -│ ├── another-skill.mdc # Generated from SKILL.md body -│ ├── SKILL.md # Manually created skill (now @reference) -│ ├── helper.py # Optional: additional resources (scripts) -│ └── reference.md # Optional: additional resources (docs) -``` - -Each skill can be defined in two ways: - -1. **Standalone `.mdc` file** - Simple skills can be a single `.mdc` file at the skills root -2. **Skill folder with `SKILL.md`** - Complex skills with additional resources - -### Bidirectional Sync - -Skiller provides bidirectional sync between `.mdc` files and `SKILL.md` folders: - -| Scenario | Sync Direction | -| ------------------------------- | ---------------------------------------------------------- | -| `.mdc` exists, no `SKILL.md` | → Generate `SKILL.md` with `@reference` to .mdc | -| `SKILL.md` body is `@reference` | .mdc is source of truth (frontmatter in SKILL.md) | -| `SKILL.md` has full content | → Generate .mdc from body, update SKILL.md to `@reference` | - -The `@reference` body pattern indicates that the `.mdc` file contains the skill content: +- Rules live in `.claude/` +- Config lives in `.claude/skiller.toml` +- Docs live in `docs/` (start at `docs/README.md`) + +## Docs + +- `docs/README.md` +- `docs/cli.md` +- `docs/config.md` +- `docs/skills.md` +- `docs/agents.md` +- `docs/mcp.md` +- `docs/troubleshooting.md` +- `docs/development.md` +- `docs/migration-from-ruler.md` + +## Supported agents + +- Claude Code +- OpenAI Codex CLI +- Cursor +- GitHub Copilot +- Aider +- Gemini CLI +- Kilo Code +- OpenCode +- RooCode +- Goose +- Amp +- Plus a bunch more: `docs/agents.md` -```yaml --- -name: my-skill -description: My custom skill ---- - -@.claude/skills/my-skill/my-skill.mdc -``` - -### Rules Migration - -Content from `.claude/rules/` is automatically migrated to `.claude/skills/` during `skiller apply`, then the rules directory is deleted: - -``` -.claude/rules/docx/ -├── SKILL.md # Makes this a skill folder -├── script.sh # Helper script -└── templates/ # Subdirectory - └── default.docx # Template file - -→ Migrated to: - -.claude/skills/docx/ -├── docx.mdc # Generated from SKILL.md body -├── SKILL.md # Updated to @reference -├── script.sh # Copied automatically -└── templates/ # Copied automatically - └── default.docx # Copied automatically - -.claude/rules/ → Deleted after migration -``` - -### Configuration - -Skills support is **enabled by default** but can be controlled via: - -**CLI flags:** - -```bash -# Enable skills (default) -skiller apply --skills - -# Disable skills -skiller apply --no-skills -``` - -**Configuration in `skiller.toml`:** - -```toml -[skills] -enabled = true # or false to disable -``` - -### Validation - -Skiller validates discovered skills and issues warnings for: - -- Missing required file (`SKILL.md`) -- Invalid directory structures (directories without `SKILL.md` and no sub-skills) - -Warnings don't prevent propagation but help identify potential issues. - -### `.gitignore` Integration - -When skills propagation is enabled, agent skills directories are automatically added to `.gitignore` (excluding `.claude/skills/` which is the committed source of truth): - -- `.codex/skills/`, `.cursor/skills/`, `.opencode/skill/`, `.agents/skills/`, `.roo/skills/`, `.gemini/skills/` - -### Dry-Run Mode - -Test skills propagation without making changes: - -```bash -skiller apply --dry-run -``` -This shows which skills would be synced, validated, and copied to each agent directory. - -### Example Workflow - -```bash -# 1. Create a skill folder -mkdir -p .claude/skills/my-skill -cat > .claude/skills/my-skill/SKILL.md << 'EOF' ---- -name: my-skill -description: My custom skill ---- - -# My Custom Skill - -This skill provides specialized knowledge for... -EOF - -# 2. Apply to sync and propagate skills -skiller apply - -# 3. Skills are now available to all compatible agents: -# - Claude Code, Copilot, Kilo Code: .claude/skills/my-skill/ -# - Codex CLI: .codex/skills/my-skill/ -# - Cursor: .cursor/skills/my-skill/ -# - OpenCode: .opencode/skill/my-skill/ -# - Goose, Amp: .agents/skills/my-skill/ -# - Roo Code: .roo/skills/my-skill/ -# - Gemini CLI: .gemini/skills/my-skill/ -``` - -## `.gitignore` Integration - -Skiller automatically manages your `.gitignore` file to keep generated agent configuration files out of version control. - -### How it Works - -- Creates or updates `.gitignore` in your project root -- Adds paths to a managed block marked with `# START Skiller Generated Files` and `# END Skiller Generated Files` -- Preserves existing content outside this block -- Sorts paths alphabetically and uses relative POSIX-style paths - -### Example `.gitignore` Section (sample - actual list depends on enabled agents) - -```gitignore -# Your existing rules -node_modules/ -*.log - -# START Skiller Generated Files -.aider.conf.yml -.clinerules -AGENTS.md -CLAUDE.md -# END Skiller Generated Files - -dist/ -``` - -### Control Options - -- **CLI flags**: `--gitignore` or `--no-gitignore` -- **Configuration**: `[gitignore].enabled` in `skiller.toml` -- **Default**: enabled - -## Practical Usage Scenarios - -### Scenario 1: Getting Started Quickly - -```bash -# Initialize Skiller in your project -cd your-project -skiller init - -# Edit the generated files -# - Add your coding guidelines to .claude/AGENTS.md (or keep adding additional .md files) -# - Customize .claude/skiller.toml if needed - -# Apply rules to all AI agents -skiller apply -``` - -### Scenario 2: Complex Projects with Nested Rules - -For large projects with multiple components or services, enable nested rule loading so each directory keeps its own rules and MCP bundle: - -```bash -# Set up nested .claude directories -mkdir -p src/.claude tests/.claude docs/.claude - -# Add component-specific instructions -echo "# API Design Guidelines" > src/.claude/api_rules.md -echo "# Testing Best Practices" > tests/.claude/test_rules.md -echo "# Documentation Standards" > docs/.claude/docs_rules.md -``` - -```toml -# .claude/skiller.toml -nested = true -``` - -```bash -# The CLI inherits nested mode from skiller.toml -skiller apply --verbose - -# Override from the CLI at any time -skiller apply --no-nested -``` - -This creates context-specific instructions for different parts of your project while maintaining global rules in the root `.claude/` directory. Nested runs automatically keep every nested config enabled even if a child tries to disable it. - -> [!NOTE] -> The CLI prints "Nested mode is experimental and may change in future releases." the first time nested processing runs. Expect refinements in future versions. - -### Scenario 3: Team Standardization - -1. Create `.claude/coding_standards.md`, `.claude/api_usage.md` -2. Commit the `.claude` directory to your repository -3. Team members pull changes and run `skiller apply` to update their local AI agent configurations - -### Scenario 4: Project-Specific Context for AI - -1. Detail your project's architecture in `.claude/project_overview.md` -2. Describe primary data structures in `.claude/data_models.md` -3. Run `skiller apply` to help AI tools provide more relevant suggestions - -### Integration with NPM Scripts - -```json -{ - "scripts": { - "skiller:apply": "skiller apply", - "dev": "npm run skiller:apply && your_dev_command", - "precommit": "npm run skiller:apply" - } -} -``` - -### Integration with GitHub Actions - -```yaml -# .github/workflows/skiller-check.yml -name: Check Skiller Configuration -on: - pull_request: - paths: ['.claude/**'] - -jobs: - check-skiller: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: '18' - cache: 'npm' - - - name: Install Skiller - run: npm install -g skiller - - - name: Apply Skiller configuration - run: skiller apply --no-gitignore - - - name: Check for uncommitted changes - run: | - if [[ -n $(git status --porcelain) ]]; then - echo "::error::Skiller configuration is out of sync!" - echo "Please run 'skiller apply' locally and commit the changes." - exit 1 - fi -``` - -## Troubleshooting - -### Common Issues - -**"Cannot find module" errors:** - -- Ensure Skiller is installed globally: `npm install -g skiller` -- Or use `npx skiller@latest` - -**Permission denied errors:** - -- On Unix systems, you may need `sudo` for global installation - -**Agent files not updating:** - -- Check if the agent is enabled in `skiller.toml` -- Verify agent isn't excluded by `--agents` flag -- Use `--verbose` to see detailed execution logs - -**Configuration validation errors:** - -- Skiller now validates `skiller.toml` format and will show specific error details -- Check that all configuration values match the expected types and formats - -### Debug Mode - -Use `--verbose` flag to see detailed execution logs: - -```bash -skiller apply --verbose -``` - -This shows: - -- Configuration loading details -- Agent selection logic -- File processing information -- MCP configuration steps +A Claude-centric fork of [ruler](https://github.com/intellectronica/ruler) with native skills support: -## FAQ +## 1. Skills as Source of Truth -**Q: Can I use different rules for different agents?** -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". +- `.claude/skills/` is the committed source of truth for skills +- **Bidirectional sync** between sibling `.mdc` and `SKILL.md` on `skiller apply` +- Sync direction is detected via the `@reference` body pattern +- `.claude/rules/` content is migrated to `.claude/skills/` and the rules directory is deleted -**Q: How do I set up different instructions for different parts of my project?** -A: Enable nested mode either by setting `nested = true` in `skiller.toml` or by passing `skiller 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 `.claude/` 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. +## 2. `CLAUDE.md` `@file` References -**Q: How do I temporarily disable Skiller for an agent?** -A: Set `enabled = false` in `skiller.toml` under `[agents.agentname]`, or use `--agents` flag to specify only the agents you want. +- Claude Code gets `CLAUDE.md` as `@relative/path` lines (one per rule file) +- Other agents still get a merged blob with `` markers -**Q: What happens to my existing agent configuration files?** -A: Skiller creates backups with `.bak` extension before overwriting any existing files. +## 3. `.mdc` File Support -**Q: Can I run Skiller in CI/CD pipelines?** -A: Yes! Use `skiller apply --no-gitignore` in CI to avoid modifying `.gitignore`. See the GitHub Actions example above. +- Reads `.md` and `.mdc` rule files +- Directory patterns auto-expand to `directory/**/*.{md,mdc}` -**Q: How does OpenHands MCP propagation classify servers?** -A: Local stdio servers become `stdio_servers`. Remote URLs containing `/sse` are classified as `sse_servers`; others become `shttp_servers`. Bearer tokens in an `Authorization` header are extracted into `api_key` where possible. +## 4. Rules Filtering -**Q: Where is Zed configuration written now?** -A: Skiller writes a `settings.json` in the project root (not the user home dir) and transforms MCP server definitions to Zed's `context_servers` format including `source: "custom"`. +- `[rules].include` / `[rules].exclude` glob patterns filter rule discovery -**Q: What changed about MCP initialization?** -A: `skiller init` now only adds example MCP server sections to `skiller.toml` instead of creating `.claude/mcp.json`. The JSON file is still consumed if present, but TOML servers win on name conflicts. +## 5. Claude Root Folder -**Q: Is Kiro supported?** -A: Yes. Kiro receives concatenated rules at `.kiro/steering/skiller_kiro_instructions.md`. +- Root directory is `.claude/` (no extra flags needed) +- Keeps Claude config (`settings.json`, `prompt.json`, hooks, etc.) together -## Development +## 6. Cursor-Style Rules -### Setup +- `rules.merge_strategy = "cursor"` parses `.mdc` frontmatter +- Only includes `.mdc` rules under `rules/` or `skills/` with `alwaysApply: true` +- Strips frontmatter and keeps body only -```bash -git clone https://github.com/udecode/skiller.git -cd skiller -npm install -npm run build -``` +## 7. Backup Control -### Testing +- `[backup].enabled = false` disables `.bak` backups -```bash -# Run all tests -npm test +## 8. Multi-Agent Skills Propagation -# Run tests with coverage -npm run test:coverage +- `.claude/skills/` is the source of truth; skills are copied to agent-native skill dirs on `skiller apply` +- Supported agent skill dirs: `.codex/skills`, `.cursor/skills`, `.opencode/skill`, `.roo/skills`, `.gemini/skills`, `.agents/skills` +- Agent skill dirs are auto-added to `.gitignore` (excluding `.claude/skills`) +- Validates skill structure and warns on missing `SKILL.md` +- Flattens nested skills into dash-separated names (e.g., `workflows/lfg` -> `workflows-lfg`) -# Run tests in watch mode -npm run test:watch -``` +## 9. Claude Code Plugins -> Skills -### Code Quality +- Reads `.claude/settings.json` `enabledPlugins` +- Loads plugin content from `~/.claude/plugins/marketplaces/...` +- Syncs enabled plugin `skills/` into agent skill dirs +- Converts plugin `commands/**/*.md` and `agents/**/*.md` into skills (`SKILL.md`) in agent skill dirs +- Local/project skills win name conflicts; plugin items are namespaced as `-` +- Tracks plugin-managed items in `.claude/.skiller.json` and removes stale items when plugins are disabled -```bash -# Run linting -npm run lint +## 10. Claude Commands/Agents -> Skills -# Run formatting -npm run format -``` +- Converts `.claude/commands/**/*.md` and `.claude/agents/**/*.md` into skills in agent skill dirs +- Local/manual skills win name conflicts; project items are namespaced as `claude-` +- Project items win over plugin items on name conflicts diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..d32f894 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,17 @@ +# Skiller docs + +Start here if you want the short, accurate version. + +- `docs/cli.md`: commands + flags +- `docs/config.md`: `skiller.toml` reference + precedence +- `docs/skills.md`: skills + propagation + Claude plugins/commands/agents sync +- `docs/agents.md`: supported agents + default paths +- `docs/mcp.md`: MCP server config + propagation +- `docs/troubleshooting.md`: common failures + fixes +- `docs/development.md`: dev workflow +- `docs/migration-from-ruler.md`: notes for `ruler` users + +Notes and design history live in: + +- `docs/plans/` +- `docs/brainstorms/` diff --git a/docs/agents.md b/docs/agents.md new file mode 100644 index 0000000..4d8f131 --- /dev/null +++ b/docs/agents.md @@ -0,0 +1,44 @@ +# Supported agents + +This is the list in `src/agents/index.ts`. + +Columns: + +- Rules: instruction file(s) written by `skiller apply` +- MCP: native MCP config file updated (if supported) +- Skills: native skills directory (if supported) + +| Identifier | Agent | Rules | MCP | Skills | +| ------------- | ----------------- | -------------------------------------------------------- | ------------------------------ | ----------------- | +| `agentsmd` | AgentsMd (pseudo) | `AGENTS.md` | - | - | +| `copilot` | GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` (`servers`) | `.claude/skills` | +| `claude` | Claude Code | `CLAUDE.md` (`@file` refs) | `.mcp.json` | `.claude/skills` | +| `codex` | OpenAI Codex CLI | `AGENTS.md`, `.codex/config.toml` | `.codex/config.toml` | `.codex/skills` | +| `cursor` | Cursor | `AGENTS.md` | `.cursor/mcp.json` | `.cursor/skills` | +| `windsurf` | Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` | - | +| `cline` | Cline | `.clinerules` | - | - | +| `aider` | Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` | - | +| `firebase` | Firebase Studio | `.idx/airules.md` | `.idx/mcp.json` | - | +| `openhands` | Open Hands | `.openhands/microagents/repo.md` | `config.toml` | - | +| `gemini-cli` | Gemini CLI | `AGENTS.md`, `.gemini/settings.json` | `.gemini/settings.json` | `.gemini/skills` | +| `jules` | Jules | `AGENTS.md` | - | - | +| `junie` | Junie | `.junie/guidelines.md` | - | - | +| `augmentcode` | AugmentCode | `.augment/rules/skiller_augment_instructions.md` | - | - | +| `kilocode` | Kilo Code | `.kilocode/rules/skiller_kilocode_instructions.md` | `.kilocode/mcp.json` | `.claude/skills` | +| `opencode` | OpenCode | `AGENTS.md`, `opencode.json` | `opencode.json` | `.opencode/skill` | +| `goose` | Goose | `.goosehints` | - | `.agents/skills` | +| `crush` | Crush | `CRUSH.md`, `.crush.json` | `.crush.json` | - | +| `amp` | Amp | `AGENTS.md` | - | `.agents/skills` | +| `zed` | Zed | `AGENTS.md`, `.zed/settings.json` | `.zed/settings.json` | - | +| `qwen` | Qwen Code | `AGENTS.md`, `.qwen/settings.json` | `.qwen/settings.json` | - | +| `kiro` | Kiro | `.kiro/steering/skiller_kiro_instructions.md` | - | - | +| `warp` | Warp | `WARP.md` | - | - | +| `roo` | RooCode | `AGENTS.md`, `.roo/mcp.json` | `.roo/mcp.json` | `.roo/skills` | +| `trae` | Trae AI | `.trae/rules/project_rules.md` | - | - | +| `amazonqcli` | Amazon Q CLI | `.amazonq/rules/skiller_q_rules.md`, `.amazonq/mcp.json` | `.amazonq/mcp.json` | - | +| `firebender` | Firebender | `firebender.json` | `firebender.json` | - | + +Notes: + +- Some agents handle MCP inside their adapter (Codex CLI, Gemini CLI, OpenCode, Crush, Zed, RooCode, Firebender, Amazon Q CLI) +- Some agents use `AGENTS.md` but do not have native MCP or skills support diff --git a/docs/brainstorms/2026-01-28-skills-as-source-of-truth-brainstorm.md b/docs/brainstorms/2026-01-28-skills-as-source-of-truth-brainstorm.md index 9c1f3ff..ac865b9 100644 --- a/docs/brainstorms/2026-01-28-skills-as-source-of-truth-brainstorm.md +++ b/docs/brainstorms/2026-01-28-skills-as-source-of-truth-brainstorm.md @@ -10,6 +10,7 @@ topic: skills-as-source-of-truth Refactor skiller to make `.claude/skills/` the committed source of truth, eliminating the intermediate `.claude/rules/` → skills generation pipeline. **New structure:** + ``` .claude/skills/ api/ @@ -18,6 +19,7 @@ Refactor skiller to make `.claude/skills/` the committed source of truth, elimin ``` **Key changes:** + - `.claude/skills/` is committed (removed from gitignore) - Drop `generate_from_rules` config option - Drop `prune` config option @@ -27,11 +29,13 @@ Refactor skiller to make `.claude/skills/` the committed source of truth, elimin ## Why This Approach The current architecture has unnecessary complexity: + 1. `.mdc` files in `.claude/rules/` generate skills in `.claude/skills/` 2. Skills are gitignored (regenerated on apply) 3. Skillz MCP provides skills to non-native agents This creates a confusing indirection. By making `.claude/skills/` the source of truth: + - Users edit `.mdc` files directly in skills folder - SKILL.md is auto-generated for Claude Code discovery - No more generation/pruning/orphan detection complexity @@ -40,6 +44,7 @@ This creates a confusing indirection. By making `.claude/skills/` the source of ## Key Decisions ### File Structure + - **SKILL.md**: Auto-generated thin wrapper containing: - Frontmatter: `name`, `description` - Body: `@.claude/skills/{name}.mdc` reference @@ -52,6 +57,7 @@ This creates a confusing indirection. By making `.claude/skills/` the source of Skiller adds `synced: true` to SKILL.md frontmatter when it syncs. **Detection logic:** + 1. If `SKILL.md` has NO `synced: true` → external install (e.g., from agentskills.io) - Sync direction: `SKILL.md` → `.mdc` - Extract body to sibling `.mdc`, add `synced: true` to SKILL.md @@ -60,22 +66,26 @@ Skiller adds `synced: true` to SKILL.md frontmatter when it syncs. - Regenerate SKILL.md from `.mdc` content This handles: + - Fresh installs from skill registries (SKILL.md has body content, no marker) - Reinstalls of the same skill (marker present, use local .mdc as source) - Normal skiller workflow (edit .mdc, apply regenerates SKILL.md) ### Migration + - If `.claude/rules/` exists on apply: 1. Migrate each `.mdc` file to `.claude/skills/{name}/SKILL.md` + sibling `.mdc` 2. Delete `.claude/rules/` folder after successful migration ### Removed Features + - `generate_from_rules` config option (always migrate if rules exist) - `prune` config option (no orphan concept anymore) - Skillz MCP server support (full code removal) - `.skillz/` directory handling ### Gitignore Changes + - Remove `.claude/skills/` from auto-added gitignore entries - Keep `.skillz/` removal (won't exist anymore) diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 0000000..12fbd2a --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,49 @@ +# CLI + +Skiller is a Node CLI (Node >= 18). + +## Install / run + +- One-off: `npx skiller@latest apply` +- Global: `npm i -g skiller` then `skiller apply` + +## `skiller init` + +Scaffolds a `.claude/` folder. + +- Creates `.claude/AGENTS.md` +- Creates `.claude/skiller.toml` +- `skiller init --global` writes to `$XDG_CONFIG_HOME/skiller` (default `~/.config/skiller`) + +## `skiller apply` + +Reads rules, selects agents, writes agent-specific files, then optionally syncs MCP, skills, and `.gitignore`. + +Flags: + +- `--project-root `: project root (default: cwd) +- `--agents `: identifiers or name substrings (case-insensitive) +- `--config `: TOML config path (default: `.claude/skiller.toml` or global) +- `--mcp` / `--no-mcp`: enable/disable MCP propagation (default: enabled) +- `--mcp-overwrite`: overwrite native MCP config instead of merging +- `--gitignore` / `--no-gitignore`: enable/disable `.gitignore` updates (default: enabled) +- `--backup` / `--no-backup`: enable/disable `.bak` backups (default: enabled) +- `--skills` / `--no-skills`: enable/disable skills (default: enabled) +- `--nested` / `--no-nested`: enable nested `.claude/` discovery (default: from TOML, otherwise off) +- `--local-only`: ignore global config +- `--dry-run`: show what would change, write nothing +- `--verbose` / `-v`: more logs + +## `skiller revert` + +Restores files from `.bak` when present, otherwise removes generated files. + +Flags: + +- `--project-root `: project root (default: cwd) +- `--agents `: revert only some agents +- `--config `: TOML config path +- `--keep-backups`: keep `.bak` after restoring +- `--local-only`: ignore global config +- `--dry-run`: show what would change, write nothing +- `--verbose` / `-v`: more logs diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 0000000..510dc27 --- /dev/null +++ b/docs/config.md @@ -0,0 +1,54 @@ +# Configuration (`skiller.toml`) + +Skiller looks for `.claude/skiller.toml` by walking up from `--project-root`. + +- If no local `.claude/skiller.toml` is found, it falls back to `$XDG_CONFIG_HOME/skiller/skiller.toml`. +- If `.claude/` exists but has no `skiller.toml`, Skiller keeps walking. + +## Agent selection precedence + +- `skiller apply --agents ...` +- `default_agents = [...]` +- `[agents.].enabled = false` disables an agent +- Otherwise: all agents are enabled + +## Rule discovery and ordering + +Skiller reads `.md` and `.mdc` under `.claude/` recursively. + +- Primary file: `.claude/AGENTS.md` if present +- Legacy fallback: `.claude/instructions.md` if `AGENTS.md` is missing +- Repository root `AGENTS.md` (outside `.claude/`) is prepended if it exists and does not look like a Skiller-generated blob +- Remaining files are appended in sorted path order + +Filtering: + +- `[rules].include` only keeps matching files +- `[rules].exclude` drops matches (wins over `include`) +- Bare directory patterns expand to `dir/**/*.{md,mdc}` + +Cursor merge strategy: + +- `rules.merge_strategy = "cursor"` keeps `AGENTS.md` plus Cursor-style `.mdc` rules under `rules/` and `skills/` with `alwaysApply: true` + +## Schema + +High level (see `docs/mcp.md` for MCP details): + +- `default_agents = ["claude", "codex"]` +- `nested = true|false` +- `[rules] include = [...], exclude = [...], merge_strategy = "all"|"cursor"` +- `[backup] enabled = true|false` +- `[gitignore] enabled = true|false` +- `[skills] enabled = true|false` +- `[mcp] enabled = true|false, merge_strategy = "merge"|"overwrite"` +- `[mcp_servers.]` server definitions +- `[agents.] enabled, output_path, output_path_instructions, output_path_config, gitignore` +- `[agents..mcp] enabled, merge_strategy` + +## MCP servers: TOML vs JSON + +- Preferred: `skiller.toml` `[mcp_servers.*]` +- Legacy: `.claude/mcp.json` is still read, but deprecated +- Merge rule: TOML wins by server name +- `${VAR}` is expanded inside `mcp_servers..env` values diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..5b89829 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,17 @@ +# Development + +## Setup + +- `pnpm install` + +## Commands + +- `pnpm test` +- `pnpm lint` +- `pnpm build` +- `pnpm format` + +## Repo conventions + +- Use feature branches +- When committing as the assistant, use `git commit --author="AI "` diff --git a/docs/mcp.md b/docs/mcp.md new file mode 100644 index 0000000..f0b9507 --- /dev/null +++ b/docs/mcp.md @@ -0,0 +1,56 @@ +# MCP (Model Context Protocol) + +Skiller can propagate MCP server configs to agents that support it. + +## Define MCP servers + +Preferred: `skiller.toml`. + +```toml +[mcp] +enabled = true +merge_strategy = "merge" # or "overwrite" + +[mcp_servers.filesystem] +command = "npx" +args = ["-y", "@modelcontextprotocol/server-filesystem", "."] + +[mcp_servers.remote_api] +url = "https://api.example.com/mcp" + +[mcp_servers.remote_api.headers] +Authorization = "Bearer ${TOKEN}" +``` + +Legacy: `.claude/mcp.json` is still read, but deprecated. + +## Filtering + transforms + +Skiller filters servers per agent capability: + +- Agents that support remote servers get `url` servers as-is +- Agents that only support stdio get remote servers transformed into stdio via `mcp-remote@latest` + +Agent-specific transforms: + +- Claude Code: `type = "remote"` is rewritten to `http` or `sse` based on the URL +- Kilo Code: `type = "remote"` is rewritten to `streamable-http` +- Firebase Studio: `type` fields are stripped before writing `.idx/mcp.json` + +## Merge vs overwrite + +- `merge` (default): shallow merge by server name +- `overwrite`: replace the whole server block for that agent + +You can override per agent: + +```toml +[agents.cursor.mcp] +enabled = true +merge_strategy = "overwrite" +``` + +## Where MCP is written + +- External propagation writes to the agent's native MCP file (see `docs/agents.md`) +- Some agents manage MCP in their own config format inside the adapter (also listed in `docs/agents.md`) diff --git a/docs/migration-from-ruler.md b/docs/migration-from-ruler.md new file mode 100644 index 0000000..165e410 --- /dev/null +++ b/docs/migration-from-ruler.md @@ -0,0 +1,10 @@ +# Coming from `ruler` + +Skiller is a fork of `intellectronica/ruler`. + +What changed: + +- Root folder is `.claude/` (not `.ruler/`) +- Claude Code gets `CLAUDE.md` as `@file` references, not a concatenated blob +- Skills are first-class: `.claude/skills/` is the committed source of truth +- Claude plugins, commands, and agents can be synced into other agents' skills dirs on `skiller apply` diff --git a/docs/plans/2026-01-28-refactor-sync-detection-via-reference-plan.md b/docs/plans/2026-01-28-refactor-sync-detection-via-reference-plan.md index 137dc1f..e9fbff8 100644 --- a/docs/plans/2026-01-28-refactor-sync-detection-via-reference-plan.md +++ b/docs/plans/2026-01-28-refactor-sync-detection-via-reference-plan.md @@ -9,6 +9,7 @@ date: 2026-01-28 ## Overview Two changes: + 1. Replace `synced: true` frontmatter flag with content-based detection - if body is just `@reference`, the referenced file is source of truth 2. Move `.mdc` files from `.claude/skills/name.mdc` to `.claude/skills/name/name.mdc` (sibling to SKILL.md) @@ -17,6 +18,7 @@ This restores compatibility with the pre-0.7 pattern and creates a cleaner file ## Problem Statement Current approach: + - Uses `synced: true` in frontmatter - extra metadata that can get out of sync - Places `.mdc` at skills root (`.claude/skills/name.mdc`) - scattered files @@ -46,12 +48,15 @@ Current approach: /** * Check if SKILL.md body is just a reference (single line starting with @). */ -function isReferenceBody(body: string): { isReference: boolean; referencePath?: string } { - const lines = body.split('\n').filter(line => line.trim().length > 0); +function isReferenceBody(body: string): { + isReference: boolean; + referencePath?: string; +} { + const lines = body.split('\n').filter((line) => line.trim().length > 0); if (lines.length === 1 && lines[0].trim().startsWith('@')) { return { isReference: true, - referencePath: lines[0].trim().slice(1) // Remove @ prefix + referencePath: lines[0].trim().slice(1), // Remove @ prefix }; } return { isReference: false }; @@ -117,7 +122,7 @@ description: My skill description --- # My Skill Content -` +`, ); // Create SKILL.md with @reference (pre-0.7 pattern) @@ -129,7 +134,7 @@ description: My skill description --- @.claude/rules/my-skill.mdc -` +`, ); const result = await syncMdcToSkillMd(skillsDir, false, false); @@ -156,7 +161,7 @@ description: My skill --- # Skill Content -` +`, ); const result = await syncMdcToSkillMd(skillsDir, false, false); @@ -166,7 +171,7 @@ description: My skill // Verify SKILL.md was created with @reference const skillMd = await fs.readFile( path.join(skillFolder, SKILL_MD_FILENAME), - 'utf8' + 'utf8', ); expect(skillMd).toContain('@./my-skill.mdc'); }); diff --git a/docs/plans/2026-01-28-skills-as-source-of-truth-plan.md b/docs/plans/2026-01-28-skills-as-source-of-truth-plan.md index bd5fc13..0a2286d 100644 --- a/docs/plans/2026-01-28-skills-as-source-of-truth-plan.md +++ b/docs/plans/2026-01-28-skills-as-source-of-truth-plan.md @@ -13,6 +13,7 @@ deepened: 2026-01-28 **Research agents used:** kieran-typescript-reviewer, pattern-recognition-specialist, architecture-strategist, code-simplicity-reviewer, data-migration-expert, security-sentinel, agent-native-reviewer, best-practices-researcher, framework-docs-researcher, skill-creator ### Key Improvements from Research + 1. **Simplified sync model** - Consider eliminating two-pass sync entirely 2. **Security fixes required** - Path traversal vulnerability in @filename expansion 3. **Type safety** - Explicit result types with discriminated unions @@ -20,6 +21,7 @@ deepened: 2026-01-28 5. **Agent accessibility** - Keep full content in SKILL.md for non-Claude agents ### Critical Decisions Required + - **Simplification opportunity**: Should we eliminate the .mdc sibling entirely and have users edit SKILL.md directly? (40-50% LOC reduction) - **MCP deprecation**: Non-Claude agents lose skill access - is this acceptable? @@ -40,6 +42,7 @@ Refactor skiller to make `.claude/skills/` the committed source of truth, elimin Location: [src/core/SkillsProcessor.ts](src/core/SkillsProcessor.ts) Remove: + - `getSkillzJsonFilePath()` (lines ~624-627) - `getSkillzMdcFilePaths()` (lines ~629-649) - `generateSkillzJsonFile()` (lines ~651-694) @@ -51,6 +54,7 @@ Remove: Non-Claude agents (Codex CLI, Windsurf, Cursor via MCP) completely lose access to skills with MCP removal. **Options:** + 1. Keep `.skillz/` copy for MCP agents (remove server config only) 2. Document that non-Claude agents lose skill support 3. Add `[skills].mcp_propagation = true` option for users who need MCP skills @@ -63,6 +67,7 @@ Removing the Skillz MCP server is a positive security change - eliminates extern Location: [src/constants.ts](src/constants.ts) Remove: + - `SKILLZ_DIR` constant - `SKILLZ_MCP_SERVER_NAME` constant @@ -71,6 +76,7 @@ Remove: Location: [src/lib.ts](src/lib.ts) Remove: + - All references to `.skillz/` directory creation - Skillz JSON file generation calls - Skillz MCP server configuration @@ -84,6 +90,7 @@ Remove any skillz-related type definitions. ### 1.5 Update Tests Remove/update tests in: + - `tests/unit/core/skillz-mcp.test.ts` (delete file) - Any other tests referencing skillz functionality @@ -107,7 +114,7 @@ export interface MdcFrontmatter { description?: string; globs?: string[]; alwaysApply?: boolean; - synced?: boolean; // NEW: Marker for sync direction detection + synced?: boolean; // NEW: Marker for sync direction detection } ``` @@ -128,6 +135,7 @@ Remove references to removed config options. Location: [src/lib.ts](src/lib.ts) Remove: + - `generate_from_rules` conditional logic (lines ~137-172, ~244-263) - `prune` conditional logic - Replace with unconditional new sync behavior @@ -210,8 +218,8 @@ interface SyncSkillsOptions { */ export async function syncSkills( skillerDir: string, - options: SyncSkillsOptions = {} -): Promise + options: SyncSkillsOptions = {}, +): Promise; ``` ### 3.3 Implement SKILL.md Generation from .mdc @@ -227,17 +235,24 @@ interface SkillMdOptions { sourceFilePath: string; } -function generateSkillMd({ name, description, sourceFilePath }: SkillMdOptions): string { +function generateSkillMd({ + name, + description, + sourceFilePath, +}: SkillMdOptions): string { // Use js-yaml to safely serialize frontmatter, avoiding YAML injection - const frontmatter = yaml.dump({ - name, - description, - synced: true, - }, { - lineWidth: -1, // No line wrapping - noRefs: true, // No YAML anchors - sortKeys: false // Preserve key order - }); + const frontmatter = yaml.dump( + { + name, + description, + synced: true, + }, + { + lineWidth: -1, // No line wrapping + noRefs: true, // No YAML anchors + sortKeys: false, // Preserve key order + }, + ); return `--- ${frontmatter.trim()} @@ -251,6 +266,7 @@ ${frontmatter.trim()} ### 3.4 Implement .mdc Extraction from SKILL.md For external installs (no `synced: true` marker): + 1. Parse SKILL.md frontmatter and body 2. Create sibling .mdc with extracted content 3. Add `synced: true` to SKILL.md @@ -259,6 +275,7 @@ For external installs (no `synced: true` marker): ### 3.5 Handle Folder Skills For skills in `.claude/skills/{name}/SKILL.md`: + - Sibling .mdc goes at `.claude/skills/{name}.mdc` (outside folder) - Reference in SKILL.md: `@.claude/skills/{name}.mdc` @@ -270,13 +287,13 @@ This structure is counterintuitive - agents looking in the skill folder won't fi From architecture review: -| Edge Case | Handling | -|-----------|----------| -| Simultaneous modification | Detect via content hash, warn user | -| Marker manually removed | Treat as standalone skill | -| New skill created manually | No marker = standalone | -| .mdc exists but SKILL.md deleted | Regenerate SKILL.md | -| Name collision (nested folders) | Throw error with clear message | +| Edge Case | Handling | +| -------------------------------- | ---------------------------------- | +| Simultaneous modification | Detect via content hash, warn user | +| Marker manually removed | Treat as standalone skill | +| New skill created manually | No marker = standalone | +| .mdc exists but SKILL.md deleted | Regenerate SKILL.md | +| Name collision (nested folders) | Throw error with clear message | --- @@ -290,7 +307,11 @@ Location: NEW FILE [src/core/SkillsMigration.ts](src/core/SkillsMigration.ts) export interface MigrationResult { success: boolean; created: Array<{ name: string; path: string }>; - skipped: Array<{ name: string; path: string; reason: 'already_exists' | 'already_synced' }>; + skipped: Array<{ + name: string; + path: string; + reason: 'already_exists' | 'already_synced'; + }>; updated: Array<{ name: string; path: string }>; warnings: string[]; error?: string; @@ -301,8 +322,8 @@ export interface MigrationResult { */ export async function migrateFromRules( skillerDir: string, - options: { dryRun?: boolean; verbose?: boolean } = {} -): Promise + options: { dryRun?: boolean; verbose?: boolean } = {}, +): Promise; ``` ### Research Insights @@ -315,6 +336,7 @@ export async function migrateFromRules( - Fix: Either inline content OR change reference to new location 2. **Add collision detection:** + ```typescript if (generatedSkillNames.has(fileName)) { throw new Error(`Duplicate skill name: ${fileName}`); @@ -322,11 +344,12 @@ export async function migrateFromRules( ``` 3. **Implement atomic migration:** + ```typescript const tempDir = `${skillsDir}.tmp-${Date.now()}`; try { await performMigration(rulesDir, tempDir); - await fs.rename(tempDir, skillsDir); // Atomic + await fs.rename(tempDir, skillsDir); // Atomic await fs.rm(rulesDir, { recursive: true }); } catch (error) { await fs.rm(tempDir, { recursive: true, force: true }); @@ -335,6 +358,7 @@ export async function migrateFromRules( ``` 4. **Create backup before deletion:** + ```typescript const backupPath = `${rulesDir}.backup-${Date.now()}`; await fs.cp(rulesDir, backupPath, { recursive: true }); @@ -343,7 +367,7 @@ export async function migrateFromRules( 5. **Add migration marker to prevent re-running:** ```typescript const migrationMarker = path.join(skillsDir, '.migrated'); - if (await fileExists(migrationMarker)) return; // Already done + if (await fileExists(migrationMarker)) return; // Already done ``` ### 4.2 Update Apply Command @@ -351,6 +375,7 @@ export async function migrateFromRules( Location: [src/lib.ts](src/lib.ts) Add migration check at start of apply: + 1. If `.claude/rules/` exists → run migration 2. After successful migration → delete rules folder 3. Proceed with normal sync @@ -364,6 +389,7 @@ Add migration check at start of apply: Location: [src/core/SkillsProcessor.ts:54-95](src/core/SkillsProcessor.ts#L54-L95) Update `getSkillsGitignorePaths()`: + - Remove `.claude/skills/` from auto-gitignore list - Keep `.skillz/` removal (cleanup for old installations) @@ -373,6 +399,7 @@ Update `getSkillsGitignorePaths()`: Committing `.claude/skills/` could expose sensitive data if users accidentally include API keys in skill content. **Recommendation:** + - Document this change prominently in migration notes - Consider adding pre-commit validation that scans for common secret patterns @@ -391,6 +418,7 @@ Ensure no references add `.claude/skills/` to gitignore. Location: [src/core/SkillsProcessor.ts:205-277](src/core/SkillsProcessor.ts#L205-L277) Remove: + - `pruneOrphanedSkills()` function - `isSkillOrphaned()` helper - All orphan detection logic @@ -415,6 +443,7 @@ Location: [src/core/SkillsUtils.ts:183-217](src/core/SkillsUtils.ts#L183-L217) **Attack:** `@../../../etc/passwd` could read arbitrary files. **Fix:** + ```typescript const absolutePath = path.resolve(projectRoot, filePath); const normalizedProjectRoot = path.resolve(projectRoot); @@ -430,14 +459,16 @@ if (!absolutePath.startsWith(normalizedProjectRoot + path.sep)) { Location: [src/core/FrontmatterParser.ts:46](src/core/FrontmatterParser.ts#L46) **Current:** + ```typescript const parsed = yaml.load(yamlContent) as Record | null; ``` **Fix:** + ```typescript const parsed = yaml.load(yamlContent, { - schema: yaml.JSON_SCHEMA // Explicit safe schema + schema: yaml.JSON_SCHEMA, // Explicit safe schema }) as Record | null; ``` @@ -446,6 +477,7 @@ const parsed = yaml.load(yamlContent, { Location: Multiple files Add `MAX_DEPTH = 50` to: + - `findAllSkillerDirs()` in FileSystemUtils.ts - `findMdcFiles()` in SkillsProcessor.ts - `walkSkillsTree()` in SkillsUtils.ts @@ -457,6 +489,7 @@ Add `MAX_DEPTH = 50` to: ### 8.1 Update Unit Tests Files to update: + - `tests/unit/core/SkillsProcessor.test.ts` - update for new sync behavior - `tests/unit/core/copy-skill-folders-from-rules.test.ts` - may need updates @@ -465,36 +498,38 @@ Files to update: ```typescript // tests/unit/core/SkillsSync.test.ts describe('syncSkills', () => { - it('creates SKILL.md with synced marker for new skills', async () => { }); - it('is idempotent - running twice produces same result', async () => { }); - it('preserves manually created skills without synced marker', async () => { }); - it('handles concurrent modifications safely', async () => { }); + it('creates SKILL.md with synced marker for new skills', async () => {}); + it('is idempotent - running twice produces same result', async () => {}); + it('preserves manually created skills without synced marker', async () => {}); + it('handles concurrent modifications safely', async () => {}); }); // tests/unit/core/SkillsMigration.test.ts describe('migrateFromRules', () => { - it('migrates .mdc files to new structure', async () => { }); - it('detects and errors on duplicate skill names', async () => { }); - it('is atomic - partial failure rolls back', async () => { }); - it('creates backup before deleting rules', async () => { }); + it('migrates .mdc files to new structure', async () => {}); + it('detects and errors on duplicate skill names', async () => {}); + it('is atomic - partial failure rolls back', async () => {}); + it('creates backup before deleting rules', async () => {}); }); // tests/unit/security/path-traversal.test.ts describe('expandAtFilenameReferences', () => { - it('blocks references outside project root', async () => { }); - it('blocks absolute path references', async () => { }); + it('blocks references outside project root', async () => {}); + it('blocks absolute path references', async () => {}); }); ``` ### 8.3 Update Integration Tests Files to update: + - `tests/integration/apply-skills.test.ts` - test new sync flow - Add new integration tests for external skill installs ### 8.4 Update E2E Tests Files to update: + - `tests/e2e/basic-apply.test.ts` - ensure new structure works --- @@ -515,6 +550,7 @@ Files to update: ## Test Verification Before each phase: + ```bash npm test npm run lint @@ -522,6 +558,7 @@ npm run build ``` After all phases: + ```bash npm ci && npm run lint && npm test && npm run build ``` @@ -531,11 +568,13 @@ npm ci && npm run lint && npm test && npm run build ## Rollback Strategy Each phase should be a separate commit. If issues arise: + 1. Revert to last working commit 2. Fix issues 3. Recommit **Migration Rollback:** + 1. Set feature flag (if kept) 2. Restore from backup 3. Re-run `skiller apply` diff --git a/docs/skills.md b/docs/skills.md new file mode 100644 index 0000000..952fe31 --- /dev/null +++ b/docs/skills.md @@ -0,0 +1,85 @@ +# Skills + +Skiller treats `.claude/skills/` as the committed source of truth. + +- Claude/Copilot/Kilo Code use `.claude/skills/` directly +- Other agents get synced copies in their native skills directories + +## Skill layout + +Canonical (sibling pattern): + +- `.claude/skills//SKILL.md` +- `.claude/skills//.mdc` + +`SKILL.md` is expected to have frontmatter. The body is either: + +- A single `@...` line pointing at the `.mdc` (wrapper mode) +- Full content (source mode) + +On `skiller apply`, Skiller normalizes skills: + +- If `.mdc` exists but `SKILL.md` is missing, it generates `SKILL.md` as a wrapper +- If `SKILL.md` has full content, it generates `.mdc` and rewrites `SKILL.md` into a wrapper +- Root-level `.mdc` files in `.claude/skills/` are migrated into the sibling pattern + +Cursor rules inside skills: + +- If `.mdc` has `alwaysApply: true` frontmatter, Skiller treats it as a Cursor rule, not a Claude skill + +## Propagation to other agents + +On `skiller apply` (when skills are enabled), Skiller copies skills into agent-native skills directories: + +- `.codex/skills` +- `.cursor/skills` +- `.opencode/skill` +- `.roo/skills` +- `.gemini/skills` +- `.agents/skills` + +Propagation rules: + +- Nested skill folders are flattened (`workflows/lfg` -> `workflows-lfg`) +- Name collisions get numeric suffixes (`foo-2`, `foo-3`, ...) +- `.mdc` files are excluded from non-Claude skill dirs +- If `SKILL.md` is a pure wrapper (`@...` only), non-Claude agents receive an inlined `SKILL.md` + +Security constraint: + +- Wrapper inlining only happens when the referenced file resolves inside the project root + +## `.claude/rules/` migration + +If skills are enabled, Skiller migrates `.claude/rules/` into `.claude/skills/` on `apply` and then deletes `.claude/rules/`. + +## Claude project commands and agents + +Skiller also syncs project-local Claude assets into agent skills directories (not into `.claude/skills/`): + +- `.claude/commands/**/*.md` -> skills +- `.claude/agents/**/*.md` -> skills + +Conflict behavior: + +- Local/manual skills win +- If a name is taken, Skiller namespaces as `claude-` (numeric suffix if needed) +- Project items can take over plugin-managed folders + +## Claude plugins + +If `.claude/settings.json` enables Claude Code plugins, Skiller syncs plugin content into agent skills directories: + +- Plugin `skills/` directories are copied as skills +- Plugin `commands/**/*.md` are converted into skills (`SKILL.md`) +- Plugin `agents/**/*.md` are converted into skills (`SKILL.md`) + +Conflict behavior: + +- Local and project names win +- Plugin items are namespaced as `-` (numeric suffix if needed) + +Tracking + cleanup: + +- Skiller tracks plugin/project-managed items in `.claude/.skiller.json` +- Disabling a plugin removes its managed items (unless the plugin is enabled but cannot be resolved on disk) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..3960532 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,42 @@ +# Troubleshooting + +## "No .claude directories found" / ".claude directory not found" + +- Run `skiller init` at your project root +- Ensure `.claude/skiller.toml` exists (Skiller only treats a `.claude/` as active if it has `skiller.toml`) + +## "Invalid configuration file format" + +Common causes: + +- Using `defaultAgents` instead of `default_agents` +- Typos in `merge_strategy` values (`merge`/`overwrite` for MCP, `all`/`cursor` for rules) + +## Skills not showing up in other agents + +- Ensure skills live under `.claude/skills//SKILL.md` +- Run `skiller apply` (skills sync happens during apply) +- Check warnings for missing `SKILL.md` (those folders are skipped) + +## Claude plugins not syncing + +- Ensure `.claude/settings.json` exists and has `enabledPlugins` +- Plugins are read from `~/.claude/plugins/marketplaces/...` +- If Skiller warns "Enabled plugin not installed", install the plugin in Claude Code first + +## I don't want Skiller touching `.gitignore` + +- Run `skiller apply --no-gitignore` +- Or set `[gitignore].enabled = false` in `skiller.toml` + +## I don't want `.bak` files + +- Run `skiller apply --no-backup` +- Or set `[backup].enabled = false` in `skiller.toml` + +## `npm install` runs `skiller apply` + +This repo's `postinstall` script runs `npx skiller@latest apply` unless `$CI` is set. + +- If you don't want that in your fork, delete the `postinstall` script +- If you only want to skip it in CI, set `CI=1` diff --git a/src/agents/AiderAgent.ts b/src/agents/AiderAgent.ts index 498e5f4..d2d4f06 100644 --- a/src/agents/AiderAgent.ts +++ b/src/agents/AiderAgent.ts @@ -85,7 +85,8 @@ export class AiderAgent implements IAgent { } getMcpServerKey(): string { - return this.agentsMdAgent.getMcpServerKey(); + // Aider uses the standard `.mcp.json` schema key. + return 'mcpServers'; } supportsMcpStdio(): boolean { diff --git a/src/core/apply-engine.ts b/src/core/apply-engine.ts index 65b76ff..616ca1a 100644 --- a/src/core/apply-engine.ts +++ b/src/core/apply-engine.ts @@ -884,9 +884,15 @@ async function applyStandardMcpConfiguration( if (dryRun) { logVerbose(`DRY RUN: Would apply MCP config to: ${dest}`, verbose); } else { - // Transform MCP config for agent-specific compatibility + // Transform MCP config for compatibility. + // + // `.mcp.json` is shared (Claude Code + others). Keep it Claude-compatible + // so later agents don't overwrite `http|sse` back to legacy `remote`. let mcpToMerge = filteredMcpJson; - if (agent.getIdentifier() === 'claude') { + if ( + agent.getIdentifier() === 'claude' || + path.basename(dest) === '.mcp.json' + ) { mcpToMerge = transformMcpForClaude(filteredMcpJson); } else if (agent.getIdentifier() === 'kilocode') { mcpToMerge = transformMcpForKiloCode(filteredMcpJson);