Skip to content

fix(github-plugin): parse CRLF frontmatter and nested author blocks - #480

Open
deanrie wants to merge 1 commit into
cursor:mainfrom
deanrie:fix/frontmatter-crlf-nested
Open

deanrie wants to merge 1 commit into
cursor:mainfrom
deanrie:fix/frontmatter-crlf-nested

Conversation

@deanrie

@deanrie deanrie commented Oct 29, 2026

Copy link
Copy Markdown

Problem

Two defects in parseFrontmatter (apps/cursor/src/lib/github-plugin/parse.ts), the parser behind every rule, skill, command and agent imported from GitHub:

  1. CRLF files are not parsed. The opening fence accepts ---\r\n, but the closing fence is searched as "\n---\n", which never occurs in a CRLF file ("\r\n---\r\n"). The function returns { data: {}, body: content }, so a Windows-authored component gets its filename as name, the first 200 characters of the raw file (starting with ---\r\nname: ...) as description, and the frontmatter inside its stored content.
  2. author: blocks are never read. The key: line is matched by the array branch before the object branch (same regex, earlier in the loop), so data.author is always [] and the indented name:/url:/avatar: lines are dropped. The author_name/author_url/author_avatar metadata the rule importer reads from data.author has therefore never been populated.

Repro on main (function extracted and run with node):

LF  : {"name":"My Rule","description":"Does things","author":[],"tags":["a","b"]}
CRLF: {}   body starts: "---\r\nname: M"

Fix

  • Strip the BOM, then normalise \r\n to \n once; everything after that works on LF.
  • Accept a closing fence on the last line of the file with no trailing newline (a rule with an empty body).
  • A bare key: opens a block; the first indented line decides whether it is a list ( - item) or a mapping ( sub: value). Scalars, quoted values and blank lines inside a block behave as before.

Same output for LF files with scalar and list keys, so existing imports are unchanged; content for CRLF files is now the LF-normalised body.

Verification

Extracted function, node with type stripping, cases: LF, CRLF, BOM + LF, fence on the last line, no frontmatter, empty block followed by a scalar, unterminated fence. CRLF now equals LF; author is { name, url }; tags is ["a","b"]; unterminated fence still returns the whole text as body. tsc --strict on the extracted function is clean and biome format is applied. No open PR changes parseFrontmatter; git merge-tree against #466, #467, #472, #476, #477 and #427 (the open PRs on this file) reports no conflicts.

parseFrontmatter accepted a CRLF opening fence but searched for an LF
closing fence, so every Windows-authored rule or skill came through with
no name or description and the raw frontmatter as its content. The
object branch for `author:` was unreachable because the array branch
matched the same `key:` line first, so author.name/url/avatar were never
read. Normalise CRLF, accept a closing fence on the last line, and decide
list vs mapping from the first indented line.

This branch has not been deployed

No deployments
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.

1 participant