clients

package
v0.14.4 Latest Latest
Warning

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

Go to latest
Published: Mar 25, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ClientIDClaudeCode    = "claude-code"
	ClientIDCursor        = "cursor"
	ClientIDCline         = "cline"
	ClientIDGemini        = "gemini"
	ClientIDGitHubCopilot = "github-copilot"
	ClientIDCodex         = "codex"
	ClientIDOpenClaw      = "openclaw"
	ClientIDKiro          = "kiro"
)

ClientID constants for supported AI coding clients

Variables

This section is empty.

Functions

func AllClientIDs added in v0.5.4

func AllClientIDs() []string

AllClientIDs returns all known client IDs

func DetectAssetType added in v0.9.0

func DetectAssetType(path string, content []byte) *asset.Type

DetectAssetType uses the global registry

func DetectClientFromContent added in v0.9.0

func DetectClientFromContent(path string, content []byte) (Client, *RuleCapabilities)

DetectClientFromContent uses the global registry

func DetectClientFromPath added in v0.9.0

func DetectClientFromPath(path string) (Client, *RuleCapabilities)

DetectClientFromPath uses the global registry

func HasAnyErrors

func HasAnyErrors(results map[string]InstallResponse) bool

HasAnyErrors checks if any client installation failed

func IsImportableFile added in v0.9.0

func IsImportableFile(path string) bool

IsImportableFile uses the global registry

func IsInstructionFile added in v0.9.0

func IsInstructionFile(path string) bool

IsInstructionFile uses the global registry

func IsRuleFile added in v0.9.0

func IsRuleFile(path string) bool

IsRuleFile uses the global registry

func IsValidClientID added in v0.5.4

func IsValidClientID(id string) bool

IsValidClientID checks if the given ID is a known client ID

func Register

func Register(client Client)

Register registers a client in the global registry

Types

type AssetBundle

type AssetBundle struct {
	Asset    *lockfile.Asset
	Metadata *metadata.Metadata
	ZipData  []byte
}

AssetBundle contains asset + metadata + zip data

type AssetResult

type AssetResult struct {
	AssetName string
	Status    ResultStatus
	Message   string
	Error     error
}

AssetResult represents the result of installing/uninstalling one asset

type BaseClient

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

BaseClient provides default implementations for common functionality

func NewBaseClient

func NewBaseClient(id, displayName string, supportedTypes []asset.Type) BaseClient

NewBaseClient creates a new base client with capabilities

func (*BaseClient) DisplayName

func (b *BaseClient) DisplayName() string

func (*BaseClient) ID

func (b *BaseClient) ID() string

func (*BaseClient) RuleCapabilities added in v0.9.0

func (b *BaseClient) RuleCapabilities() *RuleCapabilities

RuleCapabilities returns nil by default - clients override this if they support rules

func (*BaseClient) SupportsAssetType

func (b *BaseClient) SupportsAssetType(assetType asset.Type) bool

type Client

type Client interface {
	// Identity
	ID() string          // Machine name: "claude-code", "cursor", "cline"
	DisplayName() string // Human name: "Claude Code", "Cursor", "Cline"

	// Detection
	IsInstalled() bool  // Check if this client is installed/configured
	GetVersion() string // Get client version (empty if not available)

	// Capabilities - what asset types this client supports
	SupportsAssetType(assetType asset.Type) bool

	// Installation - client has FULL control over installation mechanism
	// Receives all assets to install at once (batch)
	InstallAssets(ctx context.Context, req InstallRequest) (InstallResponse, error)

	// Uninstallation - remove assets
	UninstallAssets(ctx context.Context, req UninstallRequest) (UninstallResponse, error)

	// Asset operations - for MCP server support
	// ListAssets returns all installed assets for a given scope
	ListAssets(ctx context.Context, scope *InstallScope) ([]InstalledSkill, error)
	// ReadSkill reads the content of a specific skill by name
	ReadSkill(ctx context.Context, name string, scope *InstallScope) (*SkillContent, error)

	// EnsureAssetSupport ensures asset infrastructure is set up for the current context.
	// This is called after installation to ensure rules files, MCP servers, etc. are configured.
	// For Cursor, this creates local .cursor/rules/skills.md with skills from all applicable scopes.
	// Clients that don't need post-install setup can return nil.
	EnsureAssetSupport(ctx context.Context, scope *InstallScope) error

	// GetBootstrapOptions returns bootstrap options provided by this client.
	// These are options for hooks, MCP servers, or other infrastructure the client provides.
	GetBootstrapOptions(ctx context.Context) []bootstrap.Option

	// GetBootstrapPath returns the path where bootstrap config is stored.
	// This is used for display purposes during init.
	// Returns empty string if not applicable.
	GetBootstrapPath() string

	// InstallBootstrap installs client infrastructure (hooks, MCP servers, etc.).
	// This sets up hooks for auto-update/usage tracking and registers the sx MCP server.
	// Called during installation to ensure all client infrastructure is in place.
	// The opts parameter contains only the enabled bootstrap options to install.
	// Clients that don't need bootstrap can return nil.
	InstallBootstrap(ctx context.Context, opts []bootstrap.Option) error

	// UninstallBootstrap removes client infrastructure installed by InstallBootstrap.
	// This removes hooks and unregisters the sx MCP server.
	// Called during full uninstall (--all flag) to clean up system infrastructure.
	// The opts parameter contains only the bootstrap options to uninstall.
	// Clients that don't need bootstrap can return nil.
	UninstallBootstrap(ctx context.Context, opts []bootstrap.Option) error

	// ShouldInstall checks if installation should proceed in hook mode.
	// Returns true to proceed, false to skip.
	// Called before any installation work begins.
	// For clients like Cursor that fire hooks on every prompt, this enables
	// tracking conversation IDs to only run install once per conversation.
	ShouldInstall(ctx context.Context) (bool, error)

	// VerifyAssets checks if assets are actually installed (not just tracked).
	// Used by --repair mode to detect discrepancies between tracker and filesystem.
	// Each client implements verification according to its own installation structure.
	VerifyAssets(ctx context.Context, assets []*lockfile.Asset, scope *InstallScope) []VerifyResult

	// ScanInstalledAssets scans for all installed assets of supported types.
	// Used during init to detect existing assets that could be imported into the vault.
	ScanInstalledAssets(ctx context.Context, scope *InstallScope) ([]InstalledAsset, error)

	// GetAssetPath returns the filesystem path to an installed asset.
	// Used during import to pass the asset directory to the add command.
	// Returns an error for asset types that don't have a simple directory structure.
	GetAssetPath(ctx context.Context, name string, assetType asset.Type, scope *InstallScope) (string, error)

	// RuleCapabilities returns this client's capabilities for handling rules.
	// Returns nil if the client doesn't support rules.
	RuleCapabilities() *RuleCapabilities
}

Client represents an AI coding client that can have assets installed

type InstallOptions

type InstallOptions struct {
	Force   bool // Force reinstall even if already installed
	DryRun  bool // Don't actually install, just validate
	Verbose bool // Verbose output
}

InstallOptions contains optional installation settings

type InstallRequest

type InstallRequest struct {
	Assets  []*AssetBundle // All assets to install (batch)
	Scope   *InstallScope  // Where to install (global/repo/path)
	Options InstallOptions // Additional options
}

InstallRequest contains everything needed for installation

type InstallResponse

type InstallResponse struct {
	Results []AssetResult
}

InstallResponse contains results per asset

type InstallScope

type InstallScope struct {
	Type     ScopeType // Global, Repository, Path
	RepoRoot string    // Repository root (if applicable)
	RepoURL  string    // Repository URL (if applicable)
	Path     string    // Specific path within repo (if applicable)
}

InstallScope defines where assets should be installed

type InstalledAsset added in v0.5.4

type InstalledAsset struct {
	Name        string     // Asset name
	Description string     // Asset description from metadata
	Version     string     // Asset version
	Type        asset.Type // Asset type (skill, command, agent, etc.)
}

InstalledAsset represents any asset that has been installed in a client

type InstalledSkill

type InstalledSkill struct {
	Name        string // Skill name
	Description string // Skill description from metadata
	Version     string // Skill version
}

InstalledSkill represents a skill that has been installed

type Orchestrator

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

Orchestrator coordinates installation across multiple clients

func NewOrchestrator

func NewOrchestrator(registry *Registry) *Orchestrator

NewOrchestrator creates a new installation orchestrator

func (*Orchestrator) InstallToAll

func (o *Orchestrator) InstallToAll(ctx context.Context,
	assets []*AssetBundle,
	scope *InstallScope,
	options InstallOptions) map[string]InstallResponse

InstallToAll installs assets to all detected clients concurrently

func (*Orchestrator) InstallToClients

func (o *Orchestrator) InstallToClients(ctx context.Context,
	assets []*AssetBundle,
	scope *InstallScope,
	options InstallOptions,
	targetClients []Client) map[string]InstallResponse

InstallToClients installs assets to specific clients concurrently

type ParsedRule added in v0.9.0

type ParsedRule struct {
	// Globs are the file patterns this rule applies to.
	// This is the canonical format - clients may call this "paths", "globs", or "applyTo".
	Globs []string

	// Description is a short description of the rule.
	Description string

	// ClientFields contains client-specific fields that don't have a canonical equivalent.
	// For example, Cursor's "alwaysApply" field.
	ClientFields map[string]any

	// ClientName is the name of the client that parsed this rule.
	// Empty if no client was detected.
	ClientName string

	// Content is the clean markdown content without frontmatter.
	Content string
}

ParsedRule is the canonical format returned by all client rule parsers. When a rule file is parsed, the client-specific frontmatter is converted to this common format, which can then be transformed back to client-specific format during installation.

func ParseRuleFile added in v0.9.0

func ParseRuleFile(path string, content []byte) (*ParsedRule, error)

ParseRuleFile uses the global registry

type Registry

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

Registry holds all registered clients

func Global

func Global() *Registry

Global returns the global registry

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates a new client registry

func (*Registry) DetectAssetType added in v0.9.0

func (r *Registry) DetectAssetType(path string, content []byte) *asset.Type

DetectAssetType asks clients to determine the asset type for a path. If no client claims it, falls back to generic path/content detection.

func (*Registry) DetectClientFromContent added in v0.9.0

func (r *Registry) DetectClientFromContent(path string, content []byte) (Client, *RuleCapabilities)

DetectClientFromContent asks each client if it recognizes this content. Returns the client and its capabilities, or nil if no client matches.

func (*Registry) DetectClientFromPath added in v0.9.0

func (r *Registry) DetectClientFromPath(path string) (Client, *RuleCapabilities)

DetectClientFromPath asks each client if it owns this path. Returns the client and its capabilities, or nil if no client matches.

func (*Registry) DetectInstalled

func (r *Registry) DetectInstalled() []Client

DetectInstalled returns all clients detected as installed

func (*Registry) FilterByAssetType

func (r *Registry) FilterByAssetType(assetType asset.Type) []Client

FilterByAssetType returns clients that support the given asset type

func (*Registry) Get

func (r *Registry) Get(id string) (Client, error)

Get retrieves a client by ID

func (*Registry) GetAll

func (r *Registry) GetAll() []Client

GetAll returns all registered clients

func (*Registry) GetInstructionFiles added in v0.9.0

func (r *Registry) GetInstructionFiles() []string

GetInstructionFiles returns all instruction files from all clients.

func (*Registry) GetRulesDirectories added in v0.9.0

func (r *Registry) GetRulesDirectories() []string

GetRulesDirectories returns all registered rules directories.

func (*Registry) IsImportableFile added in v0.9.0

func (r *Registry) IsImportableFile(path string) bool

IsImportableFile checks if path can be imported as rules. This includes both rule files (e.g., .claude/rules/my-rule.md) and instruction files (e.g., CLAUDE.md).

func (*Registry) IsInstructionFile added in v0.9.0

func (r *Registry) IsInstructionFile(path string) bool

IsInstructionFile checks if path matches any client's instruction files. Instruction files are files like CLAUDE.md or AGENTS.md that can contain multiple rule sections.

func (*Registry) IsRuleContent added in v0.9.0

func (r *Registry) IsRuleContent(path string, content []byte) bool

IsRuleContent checks if any client recognizes this content as a rule.

func (*Registry) IsRuleFile added in v0.9.0

func (r *Registry) IsRuleFile(path string) bool

IsRuleFile checks if any client recognizes this path as a rule file.

func (*Registry) ParseRuleFile added in v0.9.0

func (r *Registry) ParseRuleFile(path string, content []byte) (*ParsedRule, error)

ParseRuleFile finds the right client and parses the content. If no client is detected, returns the raw content as a ParsedRule.

func (*Registry) Register

func (r *Registry) Register(client Client)

Register adds a client to the registry

type ResultStatus

type ResultStatus string
const (
	StatusSuccess ResultStatus = "success"
	StatusFailed  ResultStatus = "failed"
	StatusSkipped ResultStatus = "skipped"
)

type RuleCapabilities added in v0.9.0

type RuleCapabilities struct {
	// ClientName is the identifier for this client (e.g., "claude-code", "cursor")
	ClientName string

	// RulesDirectory is where rules are stored (e.g., ".claude/rules", ".cursor/rules")
	RulesDirectory string

	// FileExtension is the file extension for rules (e.g., ".md", ".mdc")
	FileExtension string

	// InstructionFiles are files that can be parsed for rule sections (e.g., "CLAUDE.md", "AGENTS.md")
	InstructionFiles []string

	// MatchesPath checks if a path belongs to this client's rules
	MatchesPath func(path string) bool

	// MatchesContent checks if content appears to belong to this client
	MatchesContent func(path string, content []byte) bool

	// ParseRuleFile parses a rule file and returns the canonical format
	ParseRuleFile func(content []byte) (*ParsedRule, error)

	// GenerateRuleFile creates a complete rule file for this client
	GenerateRuleFile func(cfg *metadata.RuleConfig, body string) []byte

	// DetectAssetType determines what type of asset this file is.
	// Returns nil if the client doesn't recognize the file.
	// content may be nil if the file hasn't been read yet.
	DetectAssetType func(path string, content []byte) *asset.Type
}

RuleCapabilities defines what a client supports for rules. Each client registers its capabilities to enable detection, parsing, and generation.

type ScopeType

type ScopeType string
const (
	ScopeGlobal     ScopeType = "global"
	ScopeRepository ScopeType = "repo" // Must match lockfile.ScopeRepo
	ScopePath       ScopeType = "path"
)

type SkillContent

type SkillContent struct {
	Name        string // Skill name
	Description string // Skill description from metadata
	Version     string // Skill version from metadata
	Content     string // Contents of SKILL.md (or configured prompt file)
	BaseDir     string // Directory where skill is installed (for resolving @ file references)
}

SkillContent contains the full content of a skill for MCP responses

type UninstallOptions

type UninstallOptions struct {
	Force   bool // Force uninstall even if dependencies exist
	DryRun  bool // Don't actually uninstall
	Verbose bool // Verbose output
}

type UninstallRequest

type UninstallRequest struct {
	Assets  []asset.Asset
	Scope   *InstallScope
	Options UninstallOptions
}

UninstallRequest contains assets to uninstall

type UninstallResponse

type UninstallResponse struct {
	Results []AssetResult
}

UninstallResponse contains results per asset

type VerifyResult

type VerifyResult struct {
	Asset     *lockfile.Asset // The asset that was verified
	Installed bool            // Whether the asset is actually installed correctly
	Message   string          // Details about what was found or missing
}

VerifyResult represents the result of verifying a single asset's installation

Jump to

Keyboard shortcuts

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