fix(cursor): conform local plugin artifacts to real host - #131
Conversation
Emit the root layout Cursor local installs consume and replace the rejected symlink guidance with a physical-copy contract backed by pinned host evidence.
Preserve the confirmed hidden manifest layout while moving MCP to the local-plugin root and validating emitted documents against byte-vendored real-host schemas.
Record both the local evidence checkout and repository-relative source identities so future schema refreshes remain reproducible.
🦋 Changeset detectedLatest commit: ce10ebb The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 17582216a6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
commit: |
Pin the variables-capable official schema and reject portable-contract tokens so generated artifacts cannot silently become hybrids.
Cursor's 64-character plugin-name bound was only enforced by the unified `plugin` planner, so the standalone `cursor` target accepted and emitted a manifest for an over-long name. The pinned official schema (cursor/plugins@0701892) constrains the name's charset but carries no maxLength, so both planners now assert the bound through one shared message. The capability-state exhaustive `default` branches returned the capability object itself, so an untyped adapter's misspelled state read as truthy support and could enable hooks or MCP. They now raise a typed CapabilityStateError, and the registry rejects a malformed declaration at registration so a bad state never reaches `supports()` or capability intersection.
Summary
.cursor-plugin/plugin.json, explicit component pointers, rootmcp.json, plugin-scoped hooks, and${CURSOR_PLUGIN_ROOT}; reject portable Agent Plugin${PLUGIN_ROOT}/${PLUGIN_DATA}tokens instead of emitting a hybrid${VAR}used by emittedmcp.jsoninto the manifest's top-levelvariablesJSON schema while exempting Cursor's built-in root token and supported legacy alias~/.cursor/plugins/local/<name>and cite Cursor staff's cross-platform realpath-containment confirmationcursor/plugins@070189284e702e8a4d2e3cc8913994b204c5337a, retaining strict MCP/hooks schemas and immutable provenanceContract decision
The adapter now targets the Cursor Plugin format, not the portable Agent Plugin format. This matches the adapter's hook/full-component responsibilities and the known-loading TraceDecay install. The two formats are kept separate at both layout and token-validation boundaries.
Cursor's schema/tooling can drift independently (see cursor/plugins#281), so the manifest schema is commit-pinned rather than fetched from
mainduring builds.Observed vs emitted evidence
Read-only observation on 2026-08-31 against Cursor server build
9746bf00534f29fc29f1deb9ddfb5448f7905eb0and the active physical install at~/.cursor/plugins/local/tracedecay:.cursor-plugin/plugin.jsonand supports rules/agents/skills/commands/hooks/MCP/variables; portable Agent Plugin uses rootplugin.json, skills/MCP only, and different tokens.cursor-plugin/plugin.json; installed loader candidates include.cursor-plugin/plugin.json.cursor-plugin/plugin.jsonskills,hooks, andmcpServerspointers for surfaces that existmcp.json; manifest points to itmcp.json, unified used.cursor-plugin/mcp.jsonmcp.jsonand points to./mcp.jsonhooks/hooks.json. Project/user hooks are separate.cursor/hooks.jsonlocationshooks/hooks-cursor.jsonto avoid its Claude/Codexhooks/hooks.json${CURSOR_PLUGIN_ROOT}for local MCP and hook commands;${CLAUDE_PLUGIN_ROOT}is a legacy compatibility alias${CURSOR_PLUGIN_ROOT}${PLUGIN_ROOT}/${PLUGIN_DATA}so no hybrid can be emitted0701892adds top-levelvariables; custom MCP placeholders must be declared${VAR}placeholders could be emitted without manifest declarationversion; TraceDecay and generated artifacts include itSchema provenance
plugin.schema.jsoncursor/plugins@070189284e702e8a4d2e3cc8913994b204c5337a:schemas/plugin.schema.jsonvariables, plusminClientVersionsmcp.schema.jsonScriptedAlchemy/tracedecay@30e04b34d4e236d5f00fccf00eea7552dafde5a3hooks.schema.jsonThe capability table and schema provenance record the selected contract, observation date, server build, source revisions, and SHA-256 pins.
Read-only real-shape comparison
A temporary
hooks-and-scriptsCursor build (with hook + MCP surfaces) was compared without installing it:.cursor-plugin/plugin.json, rootmcp.json, plugin-scopedhooks/hooks.jsonhooks/hooks.jsonandmcp.json{ hooks, version }and version11.0.0Test plan
pnpm exec rstest run packages/agent-bundle/tests/cursor-adapter.test.ts packages/agent-bundle/tests/adapter-metadata.test.ts --config rstest.unit.config.ts(16 passed)AGENT_BUNDLE_INTEGRATION_MAX_WORKERS=1 pnpm exec rstest run packages/agent-bundle/tests/plugin-bundle.test.ts --config rstest.integration.config.ts(5 passed)pnpm --filter agent-bundle buildrslinton all changed TypeScript files (0 findings)Fixes #126
Fixes #127