Skip to content

fix(github-plugin): treat a sub-plugin dir as a literal path when filtering the tree - #467

Open
deanrie wants to merge 1 commit into
cursor:mainfrom
deanrie:fix/github-subplugin-dir-regex
Open

deanrie wants to merge 1 commit into
cursor:mainfrom
deanrie:fix/github-subplugin-dir-regex

Conversation

@deanrie

@deanrie deanrie commented Oct 23, 2026

Copy link
Copy Markdown

What

parseGitHubPlugin discovers sub-plugin directories from marketplace.json plugins[].source and from every <dir>/.cursor-plugin/plugin.json in the tree, then filters the tree with

new RegExp(`^${prefix}rules/.*\\.mdc$`)

and the same shape for agents/, skills/ and commands/. prefix is interpolated raw, so the directory name is read as a regex:

  • plugins/c++ (any +, (, ?, [ in a dir name) makes RegExp throw Invalid regular expression: Nothing to repeat. That is a plain SyntaxError, not a GitHubParseError, so it leaves parseGitHubPlugin unclassified and the import action shows a generic failure.
  • . matches any character, so plugins/foo.bar/ also matches plugins/fooXbar/.
  • A ./plugins/foo source was never normalized; it works today only because . matches the literal dot.

Repro with a stubbed fetch (tree and raw files for a marketplace with "source": "./plugins/c++", a command and a rule under it):

main:  threw: SyntaxError Invalid regular expression: /^./plugins/c++/rules/.*\.mdc$/: Nothing to repeat
fix:   components: [ 'command:format', 'rule:style' ]

Fix

  • pathUnder(prefix, pattern) escapes the prefix before building the RegExp; the four tree filters use it.
  • normalizeSubPluginDir() strips a leading ./ and surrounding slashes, so the ./ form keeps working on purpose. Tree-derived dirs never had the prefix and are unchanged.

Plain directory names produce the same matches as before, verified with the same stub against ./plugins/cpp on both versions.

Checks

The repo has no test runner, so the stub script is not committed; happy to add it if you want a place for it. tsc --noEmit -p apps/cursor/tsconfig.json reports the same two pre-existing process typing errors on this file as on main and nothing new.

One thing I noticed while here and did not touch: components from different sub-plugins are de-duplicated by type:slug alone, so two sub-plugins that both ship commands/review.md keep one and silently drop the other, while hooks get a -<prefix> suffix. Whether sub-plugin components should be namespaced the same way is your call.

…egex

parseGitHubPlugin built the per-directory matchers with
new RegExp(`^${prefix}rules/...`) where prefix is a sub-plugin dir taken
from marketplace.json plugins[].source or from the tree. Any regex
metacharacter in the dir name changed the pattern; a name like
plugins/c++ made RegExp throw "Nothing to repeat", a plain SyntaxError
that escaped parseGitHubPlugin instead of a GitHubParseError. A dot in a
dir name matched any character, and a leading ./ in source only worked
because . matched the literal dot.

pathUnder() escapes the prefix before building the RegExp, and
normalizeSubPluginDir() strips a leading ./ so that case keeps working
on purpose rather than by accident.
@vercel

vercel Bot commented Oct 23, 2026

Copy link
Copy Markdown

@deanrie is attempting to deploy a commit to the Anysphere Team on Vercel.

A member of the Team first needs to authorize it.

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