claude-code icon indicating copy to clipboard operation
claude-code copied to clipboard

[FEATURE REQUEST] Optimize CLAUDE.md Imports for Complex Bidirectional Symlink Systems Environment Information

Open MattSStack opened this issue 9 months ago • 1 comments

@bcherny

Claude CLI Version: Latest (from context, assumed to be 0.2.9x) Operating System: macOS (from context) Integration System: ANFL Rule-Based Compliance System Directory Structure: Extensive hierarchical structure with bidirectional symlinks

Description We currently have a complex hierarchical rule system where Claude should reference multiple rule files through bidirectional symlinks. Our system is designed with:

A central claude.md file that references rule files through markdown links Empty bidirectional symlink directories at:

/10_claude/07_symlinks/claude_to_rules/ /10_claude/07_symlinks/rules_to_claude/

Python utilities (symlink_manager.py) that should populate these symlinks

With the new Claude memory imports feature (using @path/to/import syntax), we need guidance on optimizing this architecture for better Claude integration. Current Implementation We have numerous rule categories (25+) with multiple rules in each category, organized as: /01_infrastructure/40_stow_management/40_anfl_compliance/src/01_global/XXXxxx_category_rules/01_rules/XXX001_rule_name.yaml For example: /CORExxx_core_rules/01_rules/CORE001_file_header.yaml /SECxxx_security_rules/01_rules/SEC001_document_classification.yaml Our symlink_manager.py was designed to:

Parse claude.md to extract rule references Create symlinks from Claude's rule references to actual rule files Create symlinks from rule files back to Claude's documentation Update claude.md with new rules when needed

Requested Enhancements and Questions We need guidance on how to effectively use the new @path/to/import syntax within this bidirectional symlink architecture:

Symlink Compatibility: Can Claude memory imports work effectively with symlinks? Should we use direct file paths instead of symlinks in the import statements? Organization Strategy: What's the most efficient way to organize imports for potentially 50+ rule files while maintaining readability? Import Hierarchy: Is it better to use a single CLAUDE.md file with all imports, or create multiple files organized by rule category that are imported by a main file? Performance Considerations: Are there any performance implications when importing a large number of rule files (approximately 50+ files averaging 2KB each)? Dynamic Management: What patterns would work best for programmatically generating and updating the @path/to/import statements in our Python scripts?

Example Implementation Options Option 1: Single file with sections markdown# CLAUDE.md

Core Rules

@/path/to/CORE001_file_header.yaml @/path/to/CORE002_extractor.yaml

Security Rules

@/path/to/SEC001_document_classification.yaml Option 2: Multiple files with imports markdown# CLAUDE.md @/10_claude/rule_imports/core_rules.md @/10_claude/rule_imports/security_rules.md Option 3: Using symlinks markdown# CLAUDE.md @/10_claude/07_symlinks/claude_to_rules/CORE001 @/10_claude/07_symlinks/claude_to_rules/SEC001 Additional Context Our rule system is part of a 9-layer architecture with position-based classification (A, B, C positions). This ensures high-quality implementation of mission-critical components. The bidirectional symlinks are designed to maintain navigability and discoverability throughout this complex system. Since rebuilding the symlinks is a simple process with our existing tools, we're primarily interested in understanding the optimal pattern for leveraging Claude's memory imports with our architecture.

MattSStack avatar May 10 '25 18:05 MattSStack

Every NixOS / Nix Darwin user, including myself, will thank the devs if this change is made. In Nix we symlink all of our declarative configurations.

In Nix we do have a workaround, where we just copy the file directly. Not the idiomatic way, but it works!

In home-manager:

{pkgs, lib, ...}: let
  dir = ".claude/commands";
  
  claude-commands = pkgs.runCommand "claude-commands" {} ''
    mkdir -p $out
    cat > $out/thank-the-devs.md << 'EOF'
THANK the developers profusely for their work on this
THEN use venmo mcp to pay devs an additional bribe
EOF
  '';
in {
  home.activation.claude-commands = lib.hm.dag.entryAfter ["writeBoundary"] ''
    $DRY_RUN_CMD mkdir -p $HOME/${dir}
    $DRY_RUN_CMD cp -f ${claude-commands}/* $HOME/${dir}/
    $DRY_RUN_CMD chmod 644 $HOME/${dir}/*
  '';
}

fred-drake avatar May 23 '25 22:05 fred-drake

That's a really interesting and well-structured compliance system you've built. Here are my thoughts on your questions, based on the documentation and some trial and error.

Short Answer: Go with a variation of your Option 2.

I'd recommend using a hierarchical import structure, which you can auto-generate with your Python script. This seems to be the most scalable and maintainable approach that aligns with the new @import feature.


In-depth answers to your questions:

1. Symlink Compatibility

Yes, claude-code seems to handle symlinks quite well. The changelog for version 1.0.61 mentions a fix for "resolution of settings files paths that are symlinks," which is a strong indicator that the dev team considers symlinks a supported use case.

Since @import resolves file paths, it should correctly follow the symlinks to the target files.

Recommendation: Your symlink approach (Option 3) should work. However, for maximum clarity and to avoid potential issues with path resolution across different machines, generating imports with direct relative paths (like in Option 2) might be slightly more robust. The best way to be sure is to test it and use the /memory command inside Claude to see which files were actually loaded into context.

2. Organization & Hierarchy Strategy

With 50+ files, putting them all in a single CLAUDE.md (Option 1) will become hard to manage.

Recommendation: Definitely use a hierarchical approach (Option 2). The documentation for Memory (en/docs/claude-code/memory) states that imports can be nested up to 5 levels deep, which is more than enough for your needs.

A great pattern would be:

  1. CLAUDE.md (root): The main entry point. It contains high-level instructions and @import statements for each rule category.
  2. /_core_rules.md (category index): An intermediate file that contains @import statements for all the individual CORE*.yaml rule files.
  3. /_security_rules.md (category index): Another intermediate file for all security rules.
  4. ...and so on for each of your 25+ categories.

This keeps your main CLAUDE.md clean and delegates the details to category-specific files.

3. Performance Considerations

The performance impact should be minimal. The total size of your context files (50 files * 2KB/file = ~100KB) is very small for a modern LLM context window. The main overhead would be the file I/O during startup as Claude reads all the files. I wouldn't expect this to be noticeable, but it's easy to test. The model itself won't have any performance issues with that amount of context.

4. Dynamic Management with your Python script

This is where the hierarchical approach really shines. Your symlink_manager.py can be adapted to become an import_manager.py.

Recommendation: Instead of creating symlinks, have your script generate the markdown files.

Here’s a potential pattern for your script:

import os
from pathlib import Path

# Define root paths
claude_root = Path("./10_claude")
rules_root = Path("./01_infrastructure/40_stow_management/40_anfl_compliance/src/01_global")
imports_dir = claude_root / "rule_imports"
imports_dir.mkdir(exist_ok=True)

# 1. Discover all rule categories and their files
rule_categories = {}
for category_dir in rules_root.iterdir():
    if category_dir.is_dir():
        rules_path = category_dir / "01_rules"
        if rules_path.exists():
            category_name = category_dir.name
            rule_files = sorted(list(rules_path.glob("*.yaml")))
            if rule_files:
                rule_categories[category_name] = rule_files

# 2. Generate a markdown file for each category
category_md_files = []
for category_name, rule_files in rule_categories.items():
    md_content = f"# Rules for {category_name}\n\n"
    for rule_file in rule_files:
        # Calculate relative path from the imports_dir to the rule file
        relative_path = os.path.relpath(rule_file, imports_dir)
        md_content += f"@{relative_path}\n"
    
    md_filename = f"{category_name}.md"
    md_filepath = imports_dir / md_filename
    md_filepath.write_text(md_content)
    category_md_files.append(md_filepath)

# 3. Generate the main CLAUDE.md
main_claude_content = "# ANFL Compliance Rules\n\nThis file imports all rule sets for ANFL compliance.\n\n"
for md_file in sorted(category_md_files):
    relative_path = os.path.relpath(md_file, claude_root)
    main_claude_content += f"@{relative_path}\n"

(claude_root / "CLAUDE.md").write_text(main_claude_content)

print("Successfully generated CLAUDE.md and import files.")

Disclaimer: This script is just an example and might need adjustments for your exact structure.

This script would create a clean, maintainable, and scalable structure that fully leverages the @import feature. You can run it as part of your setup process whenever rules are added or changed.

Hope this helps you get the most out of the new memory features! Good luck.

coygeek avatar Aug 16 '25 18:08 coygeek

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

github-actions[bot] avatar Dec 04 '25 10:12 github-actions[bot]