Skip to content

feat: support .claude/rules/ directory for scoped Claude Code rules#26

Merged
johnlindquist merged 2 commits into
johnlindquist:mainfrom
MartyJRE:feat/claude-rules-directory
Apr 14, 2026
Merged

feat: support .claude/rules/ directory for scoped Claude Code rules#26
johnlindquist merged 2 commits into
johnlindquist:mainfrom
MartyJRE:feat/claude-rules-directory

Conversation

@MartyJRE

@MartyJRE MartyJRE commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Hi! We want to use dotagent at work to keep our AI rules in sync across tools. One thing we ran into is that the Claude Code export only produces a single CLAUDE.md, which means scoped rules lose their metadata and get flattened into reference links.

Claude Code natively supports .claude/rules/ with individual .md files that have YAML frontmatter (description, globs, alwaysApply), so this PR adds that:

  • Export is now hybrid: always-apply rules → CLAUDE.md, scoped rules → .claude/rules/*.md with proper frontmatter
  • Import via new importClaudeCodeRules() reads .claude/rules/ with frontmatter parsing and maps globsscope for cross-format compatibility
  • importAll picks up both CLAUDE.md and .claude/rules/
  • CLI, gitignore patterns, and convert command all updated accordingly

The pattern follows the existing Cursor/Roo directory-based exporters. scope gets mapped to globs when globs isn't already set (since Claude calls them "globs" not "scope"). Nested rule IDs produce subdirectories (e.g., api/auth.claude/rules/api/auth.md).

All existing tests updated, new tests added for the directory export/import and round-tripping.

Summary by CodeRabbit

  • New Features

    • Claude Code export now emits always-apply rules to a top-level CLAUDE.md and scoped rules as individual files under .claude/rules/ for improved organization.
    • Auto-detection/import now recognizes .claude/rules/ directories and preserves rule metadata like description, globs/scope, and alwaysApply (default false).
  • Tests

    • Updated test suites to validate the new directory-based import/export behavior and metadata handling.

The Claude Code exporter now produces a hybrid output: always-apply
rules go to CLAUDE.md (as before), while scoped/conditional rules
are written as individual .md files in .claude/rules/ with YAML
frontmatter (description, globs, alwaysApply). This matches Claude
Code's native rules directory format.

Also adds importClaudeCodeRules() to read .claude/rules/ with
frontmatter parsing, and importAll now detects both CLAUDE.md and
.claude/rules/.
@coderabbitai

coderabbitai Bot commented Apr 7, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 80f095c2-715a-4146-9835-f5f60d48ec69

📥 Commits

Reviewing files that changed from the base of the PR and between aa99fa9 and 95a7b1c.

📒 Files selected for processing (1)
  • src/exporters.ts

📝 Walkthrough

Walkthrough

This change extends Claude Code handling from a single CLAUDE.md file to a directory-based layout under .claude/rules/. Exports split rules into always-apply (written to CLAUDE.md) and scoped rules (individual .md files). Import/export and CLI detection were updated to handle both files and directories.

Changes

Cohort / File(s) Summary
Claude Export Architecture
src/exporters.ts
Refactored exportToClaudeCode to target an output directory. Filters out private rules (unless enabled), splits rules into always-apply (written to CLAUDE.md) and scoped rules (individual .md files under .claude/rules/), emitting gray-matter frontmatter (description, globs, alwaysApply) and nested subdirectories from metadata.id.
Claude Import Capability
src/importers.ts
Added importClaudeCodeRules(rulesDir) to recursively load .claude/rules/ files, parse frontmatter, compute normalized id (strip numeric prefixes, .local, optional private segment), set alwaysApply default to false, map globsmetadata.globs and metadata.scope, and return an ImportResult. Also updated importAll to detect and call this importer.
CLI Integration Updates
src/cli.ts
Auto-detection now treats either CLAUDE.md or paths containing .claude/rules as claude. Conversion/importer dispatch uses statSync(...).isDirectory() to choose importClaudeCodeRules() for directories vs importClaudeCode() for files. Added .claude/rules/ to exported paths and updated interactive label to "Claude Code (CLAUDE.md + .claude/rules/)".
Public API Export
src/index.ts
Re-exported importClaudeCodeRules from ./importers.js to expose the new importer.
Tests — Claude Code
test/claude-code.test.ts
Updated tests to use directory-based export/import: exportToClaudeCode(rules, tempDir) and assertions that CLAUDE.md contains always-apply rules while scoped rules are emitted under tempDir/.claude/rules/ with correct frontmatter, nested ID→subdirectory mapping, and alwaysApply defaulting.
Tests — Conditional/Smoke
test/conditional-export.test.ts, test/export-conditional-smoke.test.ts
Adjusted assertions to expect file-based scoped rules under .claude/rules/ and CLAUDE.md only for always-apply content. Updated imports to new API surface and removed prior expectations about embedded conditional sections in CLAUDE.md.
Tests — .gitignore Fixture
test/gitignore-prompt-conditional.test.ts
Added .claude/rules/** to test fixture .gitignore baseline to align with new exporter gitignore patterns.

Sequence Diagrams

sequenceDiagram
    participant CLI
    participant Importers
    participant FS
    participant Parser

    Note over CLI,Importers: Importing scoped Claude rules from directory

    CLI->>Importers: importClaudeCodeRules(rulesDir)
    Importers->>FS: recurse read `.claude/rules/` -> list `.md` files
    loop per file
        Importers->>FS: read file contents
        FS-->>Importers: file + frontmatter
        Importers->>Parser: normalize id, globs, alwaysApply
        Parser-->>Importers: RuleBlock
    end
    Importers-->>CLI: ImportResult { format: "claude", filePath: rulesDir, rules[] }
Loading
sequenceDiagram
    participant Exporter
    participant Filter
    participant Writer
    participant FS

    Note over Exporter,Writer: Exporting rules to directory structure

    Exporter->>Filter: filter private (unless includePrivate)
    Filter-->>Exporter: filtered rules
    Exporter->>Filter: split by alwaysApply
    Filter-->>Exporter: [alwaysApply], [scoped]
    Exporter->>Writer: write alwaysApply → outputDir/CLAUDE.md
    Writer->>FS: create `outputDir/CLAUDE.md`
    loop per scoped rule
        Exporter->>Writer: write scoped → outputDir/.claude/rules/.../id.md
        Writer->>FS: create nested `.md` with frontmatter+content
    end
    Writer-->>Exporter: done
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

released

🐰 Hops with a carrot pen

Files once crammed in one small page,
Now rest in burrows, tidy stage by stage.
.claude/rules/ hums with order bright,
While CLAUDE.md holds the global light.
Hooray — organized in every byte! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support .claude/rules/ directory for scoped Claude Code rules' directly and clearly describes the main feature addition in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/exporters.ts`:
- Around line 388-406: In exportToClaudeCode, the mainContent written to
CLAUDE.md lacks a trailing newline; update the logic that builds/writes
mainContent (variable mainContent and the writeFileSync call that writes to
claudeMdPath) to append a single '\n' at the end before calling writeFileSync
(preserving ensureDirectoryExists and existing content assembly) so the file
always ends with a newline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a69bb0eb-96fb-4949-9ee6-e612d51e749f

📥 Commits

Reviewing files that changed from the base of the PR and between ef66926 and aa99fa9.

📒 Files selected for processing (8)
  • src/cli.ts
  • src/exporters.ts
  • src/importers.ts
  • src/index.ts
  • test/claude-code.test.ts
  • test/conditional-export.test.ts
  • test/export-conditional-smoke.test.ts
  • test/gitignore-prompt-conditional.test.ts

Comment thread src/exporters.ts
@MartyJRE

Copy link
Copy Markdown
Contributor Author

Hi @johnlindquist, do you think you could spare a second to look at this and possibly merge? Thanks!

@johnlindquist johnlindquist merged commit c4b8464 into johnlindquist:main Apr 14, 2026
2 checks passed
@MartyJRE

Copy link
Copy Markdown
Contributor Author

Thank you @johnlindquist! Sorry to ping again, but the release seems to be failing due to missing/bad npm token.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants