Skip to content

fix(core): improve SKILL.md frontmatter parsing robustness#25728

Open
NTaylorMullen wants to merge 2 commits intomainfrom
ntm/gh.25693
Open

fix(core): improve SKILL.md frontmatter parsing robustness#25728
NTaylorMullen wants to merge 2 commits intomainfrom
ntm/gh.25693

Conversation

@NTaylorMullen
Copy link
Copy Markdown
Collaborator

Summary

This PR improves the robustness of `SKILL.md` frontmatter parsing to resolve issues where skill discovery would fail due to common but "invisible" file characteristics, such as UTF-8 Byte Order Marks (BOM) or trailing spaces after markers.

Details

  • Regex Update: Modified `FRONTMATTER_REGEX` to support an optional UTF-8 BOM (`\uFEFF`) and optional trailing horizontal whitespace (`[ \t]*`) after the triple-dash markers.
  • Improved Fallback Parser: Enhanced `parseSimpleFrontmatter` to:
    • Be case-insensitive for `name:` and `description:` keys.
    • Support optional spaces before the colon (e.g., `name :`).
    • Correctly identify the end of a multi-line description by checking if the next indented line looks like a new key, preventing it from swallowing the rest of the metadata.
  • YAML Robustness: Added a fallback for `js-yaml` when non-string values (like numbers or booleans) are provided in the frontmatter, ensuring they are treated as strings by the simple parser instead of causing discovery to fail.

Related Issues

Fixes #25693

How to Validate

  1. Create a skill directory: `.gemini/skills/test-spaces`
  2. Create `SKILL.md` with a trailing space after the first `---`:
    ```

    name: test-spaces
    description: Testing trailing spaces.

    ```
  3. Run `npm run start -- skills list`.
  4. Verify `test-spaces` is discovered.

Repeat with a file containing a UTF-8 BOM at the start.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt

- Support UTF-8 BOM at the start of SKILL.md files.
- Support optional trailing spaces after frontmatter markers.
- Improve simple parser fallback to be case-insensitive and support spaces before colons.
- Prevent simple parser from swallowing subsequent keys into descriptions.
- Added unit tests for identified edge cases.

Fixes #25693
@NTaylorMullen NTaylorMullen requested a review from a team as a code owner April 21, 2026 03:34
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request improves the reliability of SKILL.md frontmatter parsing. By addressing common formatting inconsistencies and edge cases, it ensures that skill discovery remains robust even when files contain invisible characters or non-standard YAML formatting.

Highlights

  • Regex Robustness: Updated FRONTMATTER_REGEX to support optional UTF-8 BOM characters and trailing whitespace after frontmatter markers.
  • Parser Improvements: Enhanced the simple frontmatter parser to be case-insensitive, support flexible spacing around colons, and prevent multi-line descriptions from incorrectly consuming subsequent metadata keys.
  • YAML Fallback: Added a fallback mechanism to ensure non-string frontmatter values are handled gracefully by the simple parser.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances the robustness of the skill loader by improving frontmatter parsing. Key changes include support for UTF-8 BOM, handling trailing whitespace in frontmatter markers, and making the simple parser case-insensitive with support for spaces before colons. It also adds a fallback to the simple parser when YAML values are not strings. A review comment suggested refining the logic for multi-line descriptions to prevent common patterns like "Note:" from being incorrectly identified as new keys, recommending a restriction to known keys like "name" and "description".

Comment thread packages/core/src/skills/skillLoader.ts Outdated
if (nextLine.match(/^[ \t]+\S/)) {
// If next line is indented, it's a continuation of the description,
// UNLESS it looks like another key (e.g. "name:")
if (nextLine.match(/^[ \t]+\S/) && !nextLine.match(/^\s*\w+\s*:/)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The logic to prevent "swallowing" other keys into the description is too broad. The regex ^\s*\w+\s*: matches any indented line starting with a single word followed by a colon, such as "Note:" or "Example:". These are common patterns in multi-line descriptions and should not cause the parser to stop. Since this parser is specifically designed to extract "name" and "description", the check should be restricted to those known keys. Additionally, ensure the extracted string is trimmed and a fallback is used if the result is empty, consistent with repository standards for optional strings.

Suggested change
if (nextLine.match(/^[ \t]+\S/) && !nextLine.match(/^\s*\w+\s*:/)) {
if (nextLine.match(/^[ \t]+\S/) && !nextLine.match(/^\s*(name|description)\s*:/i)) {
References
  1. When using an optional string with a fallback value, trim the optional string and use the fallback if the result is empty to avoid uninformative messages from whitespace-only strings.


// Match "name:" at the start of the line (optional whitespace)
const nameMatch = line.match(/^\s*name:\s*(.*)$/);
const nameMatch = line.match(/^\s*name\s*:\s*(.*)$/i);

// Match "description:" at the start of the line (optional whitespace)
const descMatch = line.match(/^\s*description:\s*(.*)$/);
const descMatch = line.match(/^\s*description\s*:\s*(.*)$/i);
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 21, 2026

Size Change: +73 B (0%)

Total Size: 33.7 MB

Filename Size Change
./bundle/chunk-7HZ62EWV.js 0 B -14.5 MB (removed) 🏆
./bundle/chunk-7JYX7OYE.js 0 B -49.2 kB (removed) 🏆
./bundle/chunk-7LSIJV5T.js 0 B -3.8 kB (removed) 🏆
./bundle/chunk-QOIPE2QP.js 0 B -3.43 kB (removed) 🏆
./bundle/chunk-TFETZB54.js 0 B -674 kB (removed) 🏆
./bundle/chunk-USRW35AA.js 0 B -2.72 MB (removed) 🏆
./bundle/core-XRKWYEBZ.js 0 B -46.7 kB (removed) 🏆
./bundle/devtoolsService-JM7UV5K7.js 0 B -28.4 kB (removed) 🏆
./bundle/gemini-HBHBH6VI.js 0 B -576 kB (removed) 🏆
./bundle/interactiveCli-VGZMS7IN.js 0 B -1.3 MB (removed) 🏆
./bundle/liteRtServerManager-56MHLN2Q.js 0 B -2.08 kB (removed) 🏆
./bundle/oauth2-provider-UE6YCM4Y.js 0 B -9.16 kB (removed) 🏆
./bundle/chunk-ADLU5VH3.js 3.8 kB +3.8 kB (new file) 🆕
./bundle/chunk-GBGIRHJJ.js 14.5 MB +14.5 MB (new file) 🆕
./bundle/chunk-N7UMYB7S.js 49.2 kB +49.2 kB (new file) 🆕
./bundle/chunk-PYBIZ54Q.js 3.43 kB +3.43 kB (new file) 🆕
./bundle/chunk-WFPKPMM5.js 674 kB +674 kB (new file) 🆕
./bundle/chunk-ZAH77BPI.js 2.72 MB +2.72 MB (new file) 🆕
./bundle/core-QIC3S4CA.js 46.7 kB +46.7 kB (new file) 🆕
./bundle/devtoolsService-GY4VFYSR.js 28.4 kB +28.4 kB (new file) 🆕
./bundle/gemini-GMXMG5A3.js 576 kB +576 kB (new file) 🆕
./bundle/interactiveCli-QWW5GAEA.js 1.3 MB +1.3 MB (new file) 🆕
./bundle/liteRtServerManager-5OESTG4M.js 2.08 kB +2.08 kB (new file) 🆕
./bundle/oauth2-provider-5LHQZOBO.js 9.16 kB +9.16 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size Change
./bundle/bundled/third_party/index.js 8 MB 0 B
./bundle/chunk-34MYV7JD.js 2.45 kB 0 B
./bundle/chunk-5AUYMPVF.js 858 B 0 B
./bundle/chunk-5PS3AYFU.js 1.18 kB 0 B
./bundle/chunk-664ZODQF.js 124 kB 0 B
./bundle/chunk-DAHVX5MI.js 206 kB 0 B
./bundle/chunk-IUUIT4SU.js 56.5 kB 0 B
./bundle/chunk-RJTRUG2J.js 39.8 kB 0 B
./bundle/chunk-YIPDXML5.js 1.97 MB 0 B
./bundle/cleanup-GLDYQJA4.js 0 B -932 B (removed) 🏆
./bundle/devtools-36NN55EP.js 696 kB 0 B
./bundle/dist-T73EYRDX.js 356 B 0 B
./bundle/events-XB7DADIJ.js 418 B 0 B
./bundle/examples/hooks/scripts/on-start.js 188 B 0 B
./bundle/examples/mcp-server/example.js 1.43 kB 0 B
./bundle/gemini.js 4.97 kB 0 B
./bundle/getMachineId-bsd-TXG52NKR.js 1.55 kB 0 B
./bundle/getMachineId-darwin-7OE4DDZ6.js 1.55 kB 0 B
./bundle/getMachineId-linux-SHIFKOOX.js 1.34 kB 0 B
./bundle/getMachineId-unsupported-5U5DOEYY.js 1.06 kB 0 B
./bundle/getMachineId-win-6KLLGOI4.js 1.72 kB 0 B
./bundle/memoryDiscovery-NXEEFR74.js 980 B 0 B
./bundle/multipart-parser-KPBZEGQU.js 11.7 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 222 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 229 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 13.4 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B 0 B
./bundle/sandbox-macos-permissive-open.sb 890 B 0 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB 0 B
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB 0 B
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB 0 B
./bundle/sandbox-macos-strict-open.sb 4.82 kB 0 B
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB 0 B
./bundle/src-QVCVGIUX.js 47 kB 0 B
./bundle/start-UAYHAXS3.js 0 B -622 B (removed) 🏆
./bundle/tree-sitter-7U6MW5PS.js 274 kB 0 B
./bundle/tree-sitter-bash-34ZGLXVX.js 1.84 MB 0 B
./bundle/cleanup-UAB6DO5K.js 932 B +932 B (new file) 🆕
./bundle/start-J3IVPKCZ.js 622 B +622 B (new file) 🆕

compressed-size-action

- Restrict multi-line description continuation check to known keys (name, description)
- Avoid incorrectly identifying lines starting with "Note:" or similar as new keys
- Add test case to verify the fix

Fixes #25693
Copy link
Copy Markdown

@nidhishgajjar nidhishgajjar left a comment

Choose a reason for hiding this comment

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

This is a well-implemented robustness improvement for SKILL.md frontmatter parsing that addresses real-world file handling issues.

What this PR fixes:

  • Skill discovery was failing due to common but "invisible" file characteristics like UTF-8 BOM and trailing spaces
  • The simple parser was too strict and would fail on valid but slightly malformed frontmatter
  • Multi-line descriptions could incorrectly swallow other keys

Code quality:

  • Targeted improvements to regex and parsing logic (107 additions, 5 deletions, 2 files)
  • Updated FRONTMATTER_REGEX to handle UTF-8 BOM and trailing whitespace
  • Enhanced parseSimpleFrontmatter with case-insensitivity and better colon handling
  • Fixed multi-line description parsing edge cases

Testing:

  • Excellent test coverage with 5 new test cases covering:
    • UTF-8 BOM handling
    • Trailing spaces after markers
    • Case-insensitivity and space before colon
    • Prevention of key swallowing into descriptions
    • Proper handling of "Note:" in multi-line descriptions
  • All tests clearly demonstrate the issues being fixed

Documentation:

  • Clear PR description explaining the three main improvements
  • Properly references the related issue (#25693)
  • Validation steps are well-documented
  • Pre-merge checklist mostly complete (missing documentation update noted)

Robustness considerations:

  • The changes make the parser more forgiving without sacrificing correctness
  • Fallback mechanisms are properly implemented
  • Edge cases are well-covered by tests

Approval: This looks good to merge. The improvements address real parsing issues, are well-tested, and make the skill discovery more robust without introducing breaking changes.

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.

[Bug] Skills discovery fails when 'description' in SKILL.md frontmatter is a single line

3 participants