Skip to content

Cursor target artifacts don't match how Cursor actually loads local plugins (manifest location, hooks doc collision, root tokens) #126

Description

@ScriptedAlchemy

Summary

Installing a built plugin/cursor artifact into ~/.cursor/plugins/local/<name> per the generated AGENTS.md instructions produces a plugin Cursor never loads. Verified against a current Cursor build (Linux, Aug 2026) by comparing with two locally working plugins.

Evidence from working local plugins

  1. Manifest location: both working local plugins use a root plugin.json (one has no manifest at all and loads purely by convention). Nothing reads .cursor-plugin/plugin.json, which is where the adapter puts the Cursor manifest (with hooks and mcpServers pointers that are therefore dead).
  2. Hooks document collision (unified plugin target): under convention loading, Cursor would discover hooks/hooks.json — but in the unified bundle that file is the Claude/Codex document (PascalCase SessionStart/PreToolUse, ${CLAUDE_PLUGIN_ROOT} commands). The actual Cursor document is at hooks/hooks-cursor.json, referenced only from the unread .cursor-plugin/plugin.json. Net effect: silently zero Cursor hooks (or worse, a Claude-format doc parsed and ignored).
  3. Event schema itself is correct: Cursor's current first-party hook documentation confirms preToolUse + matcher: "^Shell$" + permission/updated_input/additional_context — the codec output is right; only packaging/discovery is wrong.
  4. ${CURSOR_PLUGIN_ROOT} expansion is unproven for local plugins: if Cursor doesn't export it, the emitted node "${CURSOR_PLUGIN_ROOT}/hooks/x.cursor.mjs" commands can't resolve, and AGENT_BUNDLE_PLUGIN_ROOT-anchored state resolution falls back to PATH lookups. Working local plugins use absolute or plugin-root-relative commands (user hooks run from ~/.cursor/).
  5. Working plugins also declare MCP via a root mcp.json, not .cursor-plugin/mcp.json.

Workaround used

A hand-built install directory in the tracedecay-plugin shape: root plugin.json ("hooks": "hooks/hooks.json", "skills": "skills/"), a Cursor-format hooks/hooks.json whose commands are absolute-path sh shims exporting AGENT_BUNDLE_PLUGIN_ROOT/CURSOR_PLUGIN_ROOT before exec'ing the generated *.cursor.mjs wrappers, root mcp.json with absolute args + env anchor, and a copied skills/. The generated wrappers themselves work perfectly once reachable.

Suggested fixes

  • Standalone cursor target: emit root plugin.json + root mcp.json (keep .cursor-plugin/ only if some Cursor surface actually consumes it).
  • Unified plugin target: don't let the Claude-format hooks/hooks.json shadow the Cursor doc — either a per-host hooks dir or a root Cursor manifest pointing at hooks/hooks-cursor.json explicitly.
  • Re-verify ${CURSOR_PLUGIN_ROOT} against a real local-plugin install (the capability provenance cites docs + installed marketplace plugins; local-dir plugins appear to behave differently), or emit env-pinning wrapper commands.

Observed with agent-bundle preview 560124af; consumer repo: cargo-conductor.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions