claude

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package claude implements the Claude Code adapter for agentsync.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EncodeFrontmatter

func EncodeFrontmatter(fm map[string]any, body string) ([]byte, error)

EncodeFrontmatter writes "---\n<yaml>\n---\n<body>" with the keys in fm. fm is empty: returns just the body.

func ExtraNativeKeys

func ExtraNativeKeys(raw map[string]any, modeled ...string) map[string]any

ExtraNativeKeys returns the entries of a decoded native config object whose keys are NOT in modeled — the fields agentsync does not represent in its canonical model. Adapters capture these verbatim into source.*Spec.Extra so the dest→source round-trip (import/reconcile) is not lossy for native fields agentsync doesn't understand (e.g. an MCP server's timeout/disabled/cwd). Returns nil when there are none, so an empty Extra omits the canonical [server.extra] table entirely.

func MergeExtra

func MergeExtra(spec, extra map[string]any)

MergeExtra projects passthrough native fields back into a rendered destination object. A modeled key the renderer already set always wins — Extra never clobbers a field agentsync owns — so a value that was promoted into the model can't be shadowed by a stale Extra copy.

func MergeKeys

func MergeKeys(existing, ours map[string]any, ownedPointers []string) (map[string]any, []string, []string)

MergeKeys is preserved as a re-export for backward compat with claude internal callers; new callers should import internal/jsonkeys directly.

func ParseFrontmatter

func ParseFrontmatter(data []byte) (map[string]any, string, error)

ParseFrontmatter extracts the YAML frontmatter and the markdown body. If the input doesn't begin with "---\n", returns an empty map and the entire input as body. Returns an error on any YAML decode failure; callers that want to accept Claude Code's looser "key: value-with-colons" frontmatter should use ParseFrontmatterWithReport instead.

func ParseFrontmatterWithReport

func ParseFrontmatterWithReport(data []byte) (fm map[string]any, body string, lenient bool, err error)

ParseFrontmatterWithReport is the lenient-fallback variant. It first tries strict YAML; on decode failure it falls back to a line-oriented "key: rest- of-line" parser that accepts bare colon-space inside values. lenient is true iff the strict parse failed but the lenient one succeeded — Ingest callers surface a warning in that case so the user knows their SKILL.md (or other component .md) is not strict YAML.

The lenient parser exists because Claude Code itself reads frontmatter that way: a SKILL.md whose description contains an unquoted "Triggers on: X, Y" parses fine in claude.ai but breaks sigs.k8s.io/yaml ("mapping values are not allowed in this context"). Without this fallback, the silent `continue` in adapter Ingest dropped any skill with such a description.

func SkillFileOps

func SkillFileOps(skills []source.Skill, skillsDir string) ([]adapter.FileOp, error)

SkillFileOps projects each skill into FileOps under skillsDir: the SKILL.md (frontmatter + body) plus one verbatim op per bundled file (scripts/, references/, assets/, …), preserving each bundled file's mode. It is shared by every adapter that writes skills to a skills directory (Claude, OpenCode, Codex) so the "a skill is a directory, not just SKILL.md" projection stays identical across them — when two adapters target the same skills dir, the render pipeline dedupes the byte-identical ops per path.

Types

type Adapter

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

Adapter implements adapter.Adapter for Claude Code.

func New

func New(opts Options) *Adapter

New constructs a Claude adapter.

func (*Adapter) Apply

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

Apply executes ops against Claude's native destinations. All writes route through the supplied DestWriter; we never call iox.AtomicWrite or os.Remove directly here. The DestWriter owns the foreign-collision backup invariant.

func (*Adapter) Capabilities

func (a *Adapter) Capabilities() adapter.Capability

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 native Claude 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.

func (*Adapter) IngestPlugins

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

IngestPlugins discovers the marketplaces and enabled plugins recorded in Claude's native settings.json — the documented `extraKnownMarketplaces` and `enabledPlugins` keys. It is the read side of plugin `import`: the CLI maps each result onto an agentsync marketplace source and replays `marketplace add` + `plugin install` to capture them.

The built-in `claude-plugins-official` marketplace is auto-available in Claude and is NOT listed in extraKnownMarketplaces, so it has no resolvable source here. The CLI resolves such a marketplace from agentsync's own registered marketplaces instead (run `agentsync marketplace add <source>`); only a marketplace registered in neither place is warned about and skipped.

Parsing is lenient: a missing settings.json yields no plugins, and a malformed one is treated as "no plugins discovered" rather than failing the whole import (matching the hooks/LSP blocks in Ingest). Only a genuine read error (e.g. a permission problem) is surfaced.

func (*Adapter) KeyMergeStrategy

func (a *Adapter) KeyMergeStrategy() string

KeyMergeStrategy is claude's single key-merge strategy: strict JSON (.claude.json, settings.json).

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 produces the full set of FileOps for a given resolved canonical model. Pure function: returns the same output for the same input (disk reads are treated as fixed inputs for the purposes of the merge-json-keys strategy).

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. Adapters built via the registry default to os.Stderr; commands that wrap stderr (e.g. `import` styling "warning:" labels) call this to redirect.

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. Tests inject a bytes.Buffer to assert on warnings.
	Stderr io.Writer
}

Options configure the adapter at construction.

type Paths

type Paths struct {
	Home            string // ~/.claude
	Settings        string // ~/.claude/settings.json
	DotClaude       string // ~/.claude.json (user-scope mcpServers + plugin enables live here)
	MCPProject      string // <proj>/.mcp.json (project-scope mcpServers; empty at user scope)
	SkillsDir       string // ~/.claude/skills
	AgentsDir       string // ~/.claude/agents
	CommandsDir     string // ~/.claude/commands
	Memory          string // ~/.claude/CLAUDE.md (user scope) or <proj>/CLAUDE.md (project scope)
	PluginsCacheDir string // ~/.claude/plugins/cache
}

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

func ResolvePaths

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

Jump to

Keyboard shortcuts

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