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

windsurf

package
v0.16.0 Latest Latest
Warning

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

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

Documentation

Overview

Package windsurf implements the Windsurf (Cascade) adapter for agentsync.

Windsurf's config model is asymmetric across scopes (per docs.windsurf.com, now docs.devin.ai):

  • MCP servers live ONLY in the global ~/.codeium/windsurf/mcp_config.json (a JSON `mcpServers` object). There is no project-level MCP file, so MCP is rendered at user scope and skipped (with a report) at project scope.
  • Rules (memory): project scope renders a workspace rule at .windsurf/rules/agentsync.md carrying the documented `trigger: always_on` activation frontmatter; user scope renders the single global rules file ~/.codeium/windsurf/memories/global_rules.md (always-on, frontmatter-less).
  • Workflows (slash commands) are plain markdown at BOTH scopes: .windsurf/workflows/ (project) and ~/.codeium/windsurf/global_workflows/ (user).

(Upstream now documents `.devin/rules|workflows/` as the preferred workspace layout with `.windsurf/` as the supported fallback; agentsync targets `.windsurf/`, which every released version reads.)

Skills, subagents, hooks, and LSP have no Windsurf concept and are skipped. 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 Windsurf-native server entry (the value under mcp_config.json `mcpServers.<id>`) into the canonical MCPServerSpec. A server carrying `serverUrl` (or the alternate native `url` key) is canonicalised to the http transport (Windsurf does not distinguish sse/http in the config), otherwise stdio. Native keys agentsync doesn't model are preserved in Extra.

Note the `url`→`serverUrl` canonicalization: both native keys read into the same canonical URL, but windsurfMCPSpec re-renders ONLY `serverUrl`, so a hand-authored `url` key is rewritten to `serverUrl` on the next apply. Benign — Windsurf/Devin accepts both (docs.devin.ai) — and pinned by TestIngestMCPSpec_UrlCanonicalizedToServerUrl so it can't change silently.

Types

type Adapter

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

Adapter implements adapter.Adapter for Windsurf.

func New

func New(opts Options) *Adapter

New constructs a Windsurf 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. The DestWriter owns the foreign-collision backup invariant — 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 Windsurf's native config and returns a partial source.Canonical. It is the inverse of Render, honoring the same scope asymmetry: MCP and the global rules file from `~/.codeium/windsurf/` plus global workflows at user scope; workspace rules + workflows from the project `.windsurf/` tree at project scope. The agentsync-rendered `trigger: always_on` frontmatter on the workspace rule is stripped so the canonical memory body stays byte-clean.

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 windsurf's single key-merge strategy: JSON (mcp_config.json). It is the only file whose keys agentsync co-owns; rules and workflows are whole-file markdown writes.

func (*Adapter) Name

func (a *Adapter) Name() string

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 Windsurf.

Windsurf's config is scope-asymmetric: MCP renders only at user scope (its config is global), while memory + commands render only at project scope (rules/ workflows live in the repo's .windsurf/ tree; global rules are app-managed). The non-applicable scope reports a skip for each affected item, so nothing is dropped silently. Windsurf has no native plugin enable-state agentsync models, so there is no PluginIngester; it still receives plugin-projected components on apply.

func (*Adapter) SetStderr

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

SetStderr replaces the warning sink the adapter writes Ingest warnings to. 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 ~/.codeium/windsurf 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)
	// Stderr is the warning sink for Ingest; nil means os.Stderr.
	Stderr io.Writer
	// LookPath overrides exec.LookPath for testing. nil means use exec.LookPath.
	LookPath func(file string) (string, error)
}

Options configure the adapter at construction.

type Paths

type Paths struct {
	ConfigDir   string // ~/.codeium/windsurf (user) — also the Detect probe
	MCP         string // ~/.codeium/windsurf/mcp_config.json (user scope only; "" at project)
	GlobalRules string // ~/.codeium/windsurf/memories/global_rules.md (user scope only; "" at project)
	// RulesDir is the project workspace-rules directory. Upstream rebranded
	// Windsurf → Devin Desktop (docs.windsurf.com 307-redirects to docs.devin.ai)
	// and now documents `.devin/rules/*.md` as the PREFERRED path that "takes
	// precedence", with `.windsurf/rules/*.md` kept as the still-honored legacy
	// fallback (verified against docs.devin.ai). agentsync targets the legacy
	// `.windsurf/rules/`, which every released version still reads — nothing is
	// broken. (Workflows were NOT rebranded: upstream docs still use
	// `.windsurf/workflows/`, so WorkflowsDir stays as-is.) Preferring
	// `.devin/rules/` for write+capture is a possible future enhancement.
	RulesDir     string // <proj>/.windsurf/rules (project scope only; "" at user)
	WorkflowsDir string // <proj>/.windsurf/workflows (project) / ~/.codeium/windsurf/global_workflows (user)
}

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

The fields are scope-asymmetric, mirroring Windsurf's own layout: MCP and the single global rules file are global (`~/.codeium/windsurf/`; empty at project scope), workspace rules are the project's `.windsurf/rules/` (empty at user scope), and workflows exist at BOTH scopes (project `.windsurf/workflows/`, global `~/.codeium/windsurf/global_workflows/`).

func ResolvePaths

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

ResolvePaths returns the Paths for the given target root and optional project.

Jump to

Keyboard shortcuts

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