Help us shape the future of Go! Take the 2026 Go Developer Survey

cursor

package
v0.17.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package cursor implements the Cursor adapter for agentsync.

Cursor stores its configuration under a `.cursor/` directory — user-level at ~/.cursor and project-level at <repo>/.cursor (per cursor.com/docs). Unlike Claude (which mixes many concerns into settings.json) Cursor uses dedicated JSON files per concern: MCP servers live in `.cursor/mcp.json` (the same `mcpServers` object shape Claude uses) and hooks in `.cursor/hooks.json` (a `{ "version": 1, "hooks": { … } }` document). Both are JSON, so the adapter has a single key-merge strategy (merge-json-keys), exactly as Claude owns keys in two JSON files (.claude.json + settings.json).

The remaining components are files: memory → AGENTS.md (project scope only — Cursor keeps user-level rules in app-local storage), skills → the shared `.cursor/skills/<name>/` directory, subagents → `.cursor/agents/<name>.md`, and slash commands → `.cursor/commands/<name>.md`. See docs/capability-matrix.md for the per-component coverage and documented loss.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IngestMCPSpec

func IngestMCPSpec(raw map[string]any) source.MCPServerSpec

IngestMCPSpec translates one Cursor-native MCP server table — the value under `.cursor/mcp.json` `mcpServers.<id>` — into the canonical MCPServerSpec. It is the inverse of renderMCP: type/command/args/env/url/headers carry over and any other native key (timeout, envFile, auth, …) is preserved verbatim in Extra. It stays tolerant of a `type` key on read (forward-compat) even though renderMCP no longer writes one on remote servers, so a hand-authored remote server that still carries `type` captures it rather than dropping it.

Types

type Adapter

type Adapter struct {
	// contains filtered or unexported fields
}

Adapter implements adapter.Adapter for Cursor.

func New

func New(opts Options) *Adapter

New constructs a Cursor adapter.

func (*Adapter) Apply

func (a *Adapter) Apply(ops []adapter.FileOp, w adapter.DestWriter) error

Apply routes every destination write through the supplied DestWriter rather than calling iox.AtomicWrite directly. This is the contract that keeps the foreign-collision backup guarantee honest — see the doc on adapter.DestWriter.

func (*Adapter) Detect

func (a *Adapter) Detect() (bool, error)

func (*Adapter) Ingest

func (a *Adapter) Ingest(scope adapter.Scope, project string) (source.Canonical, error)

Ingest reads Cursor's native config files and returns a partial source.Canonical. It is the inverse of Render: Ingest(Apply(Render(c))) round-trips to c for the components agentsync manages (modulo the documented projected loss — subagent tools/color, command frontmatter — which Render drops with a reported Skip).

func (*Adapter) IngestMCPSpec added in v0.15.0

func (a *Adapter) IngestMCPSpec(raw map[string]any) source.MCPServerSpec

IngestMCPSpec satisfies adapter.MCPSpecIngester by delegating to the package translator Ingest uses, so the dialect has exactly one definition.

func (*Adapter) KeyMergeStrategy

func (a *Adapter) KeyMergeStrategy() string

KeyMergeStrategy is cursor's single key-merge strategy: JSON. Both `.cursor/mcp.json` and `.cursor/hooks.json` are JSON key-merge destinations (the same strategy Claude uses for .claude.json + settings.json), so the single strategy the render pipeline uses for orphan-cleanup synthesis is correct for every key-merge path this adapter owns.

func (*Adapter) Name

func (a *Adapter) Name() string

func (*Adapter) NativeHookEvent added in v0.11.0

func (a *Adapter) NativeHookEvent(canonical string) (string, bool)

NativeHookEvent implements adapter.HookEventNamer: Cursor camelCases canonical hook events in hooks.json (PreToolUse -> preToolUse, …), so import's stale-hook retirement must disown cursor's "/hooks/<native>" state key under the NATIVE spelling — matching only the canonical name would leave the key owned with no canonical render, and the next apply's orphan cleanup would delete the event from the user's hooks.json.

func (*Adapter) RefusedHookEvents added in v0.11.0

func (a *Adapter) RefusedHookEvents(scope adapter.Scope, project string) ([]string, error)

RefusedHookEvents implements adapter.HookIngestGuard — see the interface doc for the shared contract and ingestHooks for what cursor refuses. Cursor leg: hooks.json is re-read exactly as Ingest parses it (strict JSON, UseNumber); refused events surface under their CANONICAL names (preToolUse → PreToolUse), and Cursor-only events (afterFileEdit, …) never appear — no canonical file exists to retire. Warnings are discarded here; Ingest already emitted them on the same shapes.

func (*Adapter) Render

func (a *Adapter) Render(r secrets.Resolved, scope adapter.Scope, project string) ([]adapter.FileOp, []adapter.Skip, error)

Render converts the resolved canonical into FileOps for Cursor.

Render projects each plugin's COMPONENTS (MCP, memory, skills, subagents, commands, hooks) to Cursor's native `.cursor/` paths. Cursor has a native plugin system, but where it records local enable-state is undocumented, so the adapter implements no PluginIngester yet and never re-emits any enable-state on apply — the same read-is-discovery / apply-fans-out-components invariant every adapter follows (see docs/architecture.md § "PluginIngester (read-only)").

func (*Adapter) SetStderr

func (a *Adapter) SetStderr(w io.Writer)

SetStderr replaces the warning sink the adapter writes Ingest warnings to, so a CLI command can route adapter warnings through the same styled writer it uses for its own output. See claude.Adapter.SetStderr for the contract.

func (*Adapter) VersionRoots added in v0.7.5

func (a *Adapter) VersionRoots(scope adapter.Scope, project string) []string

VersionRoots implements adapter.VersionedDirs: the user-scope ~/.cursor config dir is the local-only git-backup root (issue #118). Returns nil at project scope.

type Options

type Options struct {
	TargetRoot string // honors AGENTSYNC_TARGET_ROOT (real "/Users/x" in production)
	// LookPath overrides exec.LookPath for testing. nil means use exec.LookPath.
	LookPath func(file string) (string, error)
	// Stderr receives Ingest warnings (lenient-YAML notices, dropped components).
	// nil means os.Stderr.
	Stderr io.Writer
}

Options configure the adapter at construction.

type Paths

type Paths struct {
	ConfigDir   string // ~/.cursor (or <proj>/.cursor)
	MCP         string // .cursor/mcp.json  (mcpServers; merge-json-keys)
	Hooks       string // .cursor/hooks.json ({version, hooks}; merge-json-keys)
	Memory      string // <proj>/AGENTS.md at project scope; "" at user scope
	SkillsDir   string // .cursor/skills (skill directories)
	AgentsDir   string // .cursor/agents (subagent markdown)
	CommandsDir string // .cursor/commands (slash-command markdown)
}

Paths resolves the destination paths for a given (scope, project, target-root).

Note SkillsDir lives under ~/.cursor/skills (Cursor reads personal skills from $HOME/.cursor/skills as well as the shared ~/.agents/skills). Memory is empty at user scope: Cursor keeps user-level rules in app-local storage, so there is no user-scope filesystem target — only project-scope memory lands as the repo-root AGENTS.md (which Cursor reads natively).

func ResolvePaths

func ResolvePaths(targetRoot, project string, projectScope bool) Paths

ResolvePaths returns the Paths for the given target root and optional project. projectScope=true + non-empty project uses project-local .cursor/ dirs and the repo's AGENTS.md.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL