Skip to content

fix: improve YAML parsing for custom modes to be more lenient #7139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

roomote[bot]
Copy link

@roomote roomote bot commented Aug 16, 2025

Summary

This PR fixes issue #7138 where custom modes would disappear or fail to load due to overly strict YAML parsing and validation.

Problem

Users were experiencing:

  • Custom modes disappearing after minor edits
  • YAML parsing being too strict and failing on minor syntax issues
  • .roomodes files not accepting JSON format as fallback
  • Unhelpful error messages when YAML parsing failed

Solution

Implemented a multi-layered approach to make custom mode loading more robust:

1. Lenient YAML Parsing

  • Added strict: false and uniqueKeys: false options to YAML parser
  • Allows duplicate keys (last one wins) and other minor issues

2. Multiple Fallback Strategies

For .roomodes files, implemented fallback chain:

  1. Try parsing as YAML with lenient options
  2. Try parsing as JSON (for backward compatibility)
  3. Try parsing original content without preprocessing
  4. Attempt to extract partial modes from corrupted YAML
  5. Return empty array with helpful error message if all fail

3. Mode Recovery Mechanisms

  • Automatically fix common issues before validation (trim whitespace, fix groups format)
  • Provide default values for missing required fields
  • Recover modes with minimal required fields (slug) by generating sensible defaults
  • Filter out invalid group names while preserving valid ones

4. Better Error Handling

  • More helpful error messages with specific suggestions
  • Warning messages for partially invalid modes (load what's valid)
  • Preserve complex group syntax with fileRegex

Testing

  • Added comprehensive test suite for lenient parsing scenarios
  • Tests for JSON fallback, partial recovery, missing fields, invalid groups
  • All existing tests pass
  • New tests cover edge cases and recovery mechanisms

Impact

Users should now experience:

  • More resilient custom mode loading
  • Better recovery from syntax errors
  • Helpful error messages when issues occur
  • No more disappearing modes after minor edits

Fixes #7138


Important

Improves YAML parsing for custom modes with lenient parsing, fallback strategies, and comprehensive testing.

  • Behavior:
    • parseYamlSafely() in CustomModesManager.ts now uses lenient YAML parsing with strict: false and uniqueKeys: false.
    • Implements fallback strategies for .roomodes files: JSON parsing, original content parsing, partial mode extraction.
    • Adds recovery mechanisms for modes with missing fields or invalid groups.
    • Provides more helpful error messages and warnings for invalid modes.
  • Testing:
    • Adds CustomModesManager.lenientParsing.spec.ts to test lenient parsing, JSON fallback, and recovery mechanisms.
    • Adds CustomModesManager.yamlEdgeCases.spec.ts to test edge cases like BOM handling, invisible characters, and complex structures.
  • Misc:
    • Updates loadModesFromFile() to handle single mode objects and invalid arrays.
    • Enhances fixCommonModeIssues() and recoverInvalidMode() for better mode validation and recovery.

This description was created by Ellipsis for 7bda2dc. You can customize this summary. It will automatically update as commits are pushed.

- Add lenient YAML parsing with strict:false and uniqueKeys:false options
- Implement multiple fallback strategies for .roomodes files (JSON, original content, partial extraction)
- Add recovery mechanisms for invalid/incomplete mode configurations
- Provide default values for missing required fields
- Fix handling of various group formats (string, array, complex with fileRegex)
- Improve error messages with helpful suggestions
- Add comprehensive tests for edge cases and recovery scenarios

Fixes #7138
@roomote roomote bot requested review from mrubens, cte and jr as code owners August 16, 2025 01:27
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. bug Something isn't working labels Aug 16, 2025
Copy link
Author

@roomote roomote bot left a comment

Choose a reason for hiding this comment

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

Reviewing my own code is like debugging in a mirror - everything looks backwards but the bugs are still mine.

private extractPartialModes(content: string): any {
try {
const modes: any[] = []
const lines = content.split("\n")
Copy link
Author

Choose a reason for hiding this comment

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

The extractPartialModes() method iterates through every line of potentially large YAML files. For very large corrupted files, this could cause performance issues. Consider adding a size limit or line count limit (e.g., max 10,000 lines) to prevent hanging on massive files.

// Try parsing the original content as JSON (not the cleaned content)
return JSON.parse(content)
const jsonParsed = JSON.parse(content)
console.log(`[CustomModesManager] Successfully parsed ${filePath} as JSON`)
Copy link
Author

Choose a reason for hiding this comment

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

For consistency, consider using the logger utility (already imported at line 14) instead of console.log:

Suggested change
console.log(`[CustomModesManager] Successfully parsed ${filePath} as JSON`)
logger.info(`[CustomModesManager] Successfully parsed ${filePath} as JSON`)

strict: false,
uniqueKeys: false,
})
console.log(`[CustomModesManager] Successfully parsed ${filePath} with original content`)
Copy link
Author

Choose a reason for hiding this comment

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

Similarly here, consider using logger.info for consistency:

Suggested change
console.log(`[CustomModesManager] Successfully parsed ${filePath} with original content`)
logger.info(`[CustomModesManager] Successfully parsed ${filePath} with original content`)

// Strategy 3: Try to extract valid modes even from partially corrupted YAML
const partialModes = this.extractPartialModes(content)
if (partialModes && partialModes.customModes && partialModes.customModes.length > 0) {
console.log(
Copy link
Author

Choose a reason for hiding this comment

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

And here as well:

Suggested change
console.log(
logger.info(
`[CustomModesManager] Extracted ${partialModes.customModes.length} modes from partially corrupted YAML`,
)


return modes.length > 0 ? { customModes: modes } : null
} catch (error) {
console.error("[CustomModesManager] Failed to extract partial modes:", error)
Copy link
Author

Choose a reason for hiding this comment

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

After logging the error, should we return null immediately instead of continuing? The current approach might mask other issues:

Suggested change
console.error("[CustomModesManager] Failed to extract partial modes:", error)
logger.error("[CustomModesManager] Failed to extract partial modes:", error)
return null

@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Aug 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. size:XL This PR changes 500-999 lines, ignoring generated files.
Projects
Status: Triage
Development

Successfully merging this pull request may close these issues.

[Bug] 3.25.15 on macOS creating new custom mode is too strict in yaml to understand
2 participants