Skip to content

Mode export/import: Rules files are written to wrong directory when slug is changed #6229

@hannesrudolph

Description

@hannesrudolph

Description

When exporting a Roo mode, the relativePath for rule files includes the rules-<slug> prefix (e.g., rules-old-slug/rule.xml). If a user changes the slug in the exported YAML file before importing, the rules are written to a directory based on the old slug instead of the new one.

Steps to Reproduce

  1. Export a mode that has rules files (e.g., slug: "old-slug")
  2. Open the exported YAML file
  3. Change the slug to a new value (e.g., "new-slug")
  4. Import the modified YAML file
  5. Check the .roo directory

Expected Behavior

Rules files should be imported into .roo/rules-new-slug/ based on the slug in the YAML file.

Actual Behavior

Rules files are created in .roo/rules-old-slug/ (using the path from the export), while .roo/rules-new-slug/ is deleted but remains empty.

Code Investigation

  • Export function (CustomModesManager.ts:787-788): Calculates relativePath including the rules folder prefix
  • Import function (CustomModesManager.ts:884): Uses the exported path directly without adapting to the new slug

Impact

Users cannot rename modes by editing the exported YAML, which limits the reusability of exported modes. This affects anyone trying to:

  • Share modes with different names
  • Create variations of existing modes
  • Organize modes with consistent naming

🔍 Comprehensive Issue Scoping

Root Cause / Implementation Target

The relativePath in exported mode YAML files includes the rules-<slug> prefix, making it dependent on the mode's slug. When users change the slug before importing, the rules files are written to the wrong directory (using the old slug) while the new slug's directory is deleted.

Affected Components

  • Primary Files:

    • src/core/config/CustomModesManager.ts (lines 786-788, 872-902): Export and import logic
    • src/core/config/__tests__/CustomModesManager.spec.ts: Test expectations need updating
  • Secondary Impact:

    • src/services/marketplace/SimpleInstaller.ts: Uses importModeWithRules
    • src/core/webview/webviewMessageHandler.ts: Handles export/import UI actions
    • No changes needed in secondary files

Current Implementation Analysis

The export function at line 787-788 calculates relativePath as:

const relativePath = isGlobalMode
    ? path.relative(baseDir, filePath)
    : path.relative(path.join(baseDir, ".roo"), filePath)

This results in paths like rules-old-slug/rule.md being stored in the YAML.

The import function at line 884 uses this path directly:

const targetPath = path.join(baseDir, normalizedRelativePath)

This creates files at the old slug's location instead of the new one.

Proposed Implementation

Step 1: Fix Export Path Calculation

  • File: src/core/config/CustomModesManager.ts
  • Line: 787-788
  • Change: Use path.relative(modeRulesDir, filePath) to make paths relative to the rules directory
  • Rationale: This makes relativePath slug-independent (e.g., rule.md instead of rules-slug/rule.md)

Step 2: Add Backwards Compatibility to Import

  • File: src/core/config/CustomModesManager.ts
  • Lines: 872-884
  • Changes:
    // Strip rules-<slug> prefix for backwards compatibility
    let cleanRelativePath = ruleFile.relativePath
    const rulesPrefix = cleanRelativePath.match(/^rules-[^\/]+\/(.*)$/)
    if (rulesPrefix) {
        cleanRelativePath = rulesPrefix[1]
    }
  • Then use path.join(rulesFolderPath, normalizedRelativePath) for the target path
  • Rationale: Supports both old and new export formats seamlessly

Code Architecture Considerations

  • The solution follows existing patterns for path handling
  • Maintains all security validations (path traversal prevention)
  • No new dependencies or architectural changes needed
  • Backwards compatibility ensures no breaking changes

Testing Requirements

  • Unit Tests:
    • Test export creates slug-independent paths
    • Test import with new format works correctly
    • Test import with old format (backwards compatibility)
    • Test slug renaming scenario
    • Test nested directory handling
  • Integration Tests:
    • Test full export/import cycle with slug change
  • Edge Cases:
    • Empty rules directory
    • Path traversal attempts
    • Multiple rules- prefixes in path

Performance Impact

  • Expected performance change: Neutral
  • Benchmarking needed: No
  • Optimization opportunities: None identified

Security Considerations

  • Path traversal prevention already in place (lines 876-892)
  • No new security risks introduced
  • Backwards compatibility regex is safe and specific

Migration Strategy

No migration needed - backwards compatibility handles all existing exports automatically.

Rollback Plan

If issues arise, revert the two changed methods in CustomModesManager.ts.

Dependencies and Breaking Changes

  • External dependencies affected: None
  • API contract changes: None (same YAML structure, just different paths)
  • Breaking changes for users: None (backwards compatible)

Metadata

Metadata

Assignees

Labels

Issue - In ProgressSomeone is actively working on this. Should link to a PR soon.bugSomething isn't working

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions