platform

package
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Index

Constants

View Source
const PluginManifestName = "PLUGIN.yaml"

Variables

View Source
var BackupBeforeOverwrite = func(dst string) error { return sidecarBackup(stdPlatformIO{}, dst) }

BackupBeforeOverwrite preserves an existing managed-file destination before writeManagedFile replaces it with freshly rendered content. The default writes a sibling <dst>.dot-agents-backup (the existing repo backup convention, headless-safe, no layering inversion). The future config-distribution / lock-file model can wire a richer mirror-backup adapter through this seam without touching internal/platform.

This is a deliberate forward-compat extension point (NOT a test seam) — see the docstring above. Tests swap it via the legacy func-var pattern because that pattern matches its intended runtime-swap contract; the seam-interface-di-migration plan does not target this var.

View Source
var PluginManifestSchema = schemas.Plugin

PluginManifestSchema is the embedded compiled schema for PLUGIN.yaml.

Functions

func CanonicalBucketPath

func CanonicalBucketPath(bucket CanonicalBucket, parts ...string) string

CanonicalBucketPath returns a slash-normalized canonical registry key (not a filesystem path). Keys are stored and compared in POSIX form on every OS, so the join is forced to forward slashes.

func CanonicalBucketRoot

func CanonicalBucketRoot(agentsHome string, bucket CanonicalBucket) string

func CanonicalBucketScopePath

func CanonicalBucketScopePath(bucket CanonicalBucket, scope string, parts ...string) string

CanonicalBucketScopePath returns a slash-normalized canonical registry key (not a filesystem path). See CanonicalBucketPath.

func CanonicalBucketScopeRoot

func CanonicalBucketScopeRoot(agentsHome string, bucket CanonicalBucket, scope string) string

func CollectAndExecuteSharedTargetPlan

func CollectAndExecuteSharedTargetPlan(project, repoPath string, platforms []Platform) error

CollectAndExecuteSharedTargetPlan runs BuildSharedTargetPlan then executes it against the repo and agents home. This is the command-layer entry point for centralized shared-target writes.

func DryRunSharedTargetPlanLines

func DryRunSharedTargetPlanLines(project, repoPath string, platforms []Platform) ([]string, error)

DryRunSharedTargetPlanLines describes what CollectAndExecuteSharedTargetPlan would write (merged shared-target rows, duplicate-intent counts) without touching the filesystem.

func EnsureUnderMCPScopeTree

func EnsureUnderMCPScopeTree(agentsHome, scope, target string) error

EnsureUnderMCPScopeTree checks that target is under agentsHome/mcp/scope.

func EnsureUnderRulesScopeTree

func EnsureUnderRulesScopeTree(agentsHome, scope, target string) error

EnsureUnderRulesScopeTree checks that target is under agentsHome/rules/scope (after clean).

func EnsureUnderSettingsScopeTree

func EnsureUnderSettingsScopeTree(agentsHome, scope, target string) error

EnsureUnderSettingsScopeTree checks that target is under agentsHome/settings/scope.

func ExecuteSharedSkillMirrorPlan

func ExecuteSharedSkillMirrorPlan(project, repoPath string, targetRoots ...string) error
func HasMultipleHardLinks(path string) bool

HasMultipleHardLinks is the exported entry point that the commands/internal/lifecycle HasMultipleHardLinks func-var seam now delegates to. Exported (not package-private) because lifecycle's backup.go and status.go need to read it across the package boundary, and because backup_test.go overrides the lifecycle-side seam directly — both callers are within the dot-agents binary so the surface stays internal.

func ListScopedResourceDirsForBucket

func ListScopedResourceDirsForBucket(agentsHome string, bucket CanonicalBucket, scope, marker string) ([]resourceDir, error)

func RemoveSharedTargetPlan

func RemoveSharedTargetPlan(project, repoPath string, platforms []Platform) error

RemoveSharedTargetPlan removes repo-local shared targets implied by the merged plan for the given platforms (same aggregation as CollectAndExecuteSharedTargetPlan). Symlinks are removed only when they point into agentsHome; rendered files are removed for known materializers (e.g. codex-agent-toml).

func ResolveHookCommand

func ResolveHookCommand(spec HookSpec) string

ResolveHookCommand returns the hook command with relative paths resolved against the HOOK.yaml location.

func RunSharedTargetProjection

func RunSharedTargetProjection(project, repoPath string, platforms []Platform, dryRun bool) ([]string, error)

RunSharedTargetProjection is the command-layer entry point for shared-target projection: it builds the merged ResourcePlan (BuildSharedTargetPlan) and either returns dry-run preview lines or executes writes. This keeps refresh/install/add on one code path for "build intents → plan → dry-run or apply".

Callers must set config.SetWindowsMirrorContext(repoPath) before calling when the repo needs Windows-specific path behavior for intent resolution.

func RunSharedTargetProjectionExact added in v0.4.0

func RunSharedTargetProjectionExact(project, repoPath string, platforms []Platform, dryRun, exact bool) ([]string, error)

RunSharedTargetProjectionExact is the EXACT/PRUNE command-layer entry point (config-v2-coherence §7A.5 / D10 "outputs half"): it projects the resolved asset-store union AND prunes managed outputs that are no longer in the resolved set, so the repo tree converges to exactly what the plan declares. It is the projection refresh/install drive by default; pass exact=false (`--inexact`) to keep the additive RunSharedTargetProjection behavior (write the wanted set, leave stale managed outputs in place).

Dry-run returns the same preview lines as RunSharedTargetProjection plus a "prune" line for every managed output the apply path would delete, so the preview is a faithful diff of the exact projection. Apply executes the plan then prunes; prune is best-effort relative to the write — a prune failure is returned so the caller can withhold a clean-success stamp.

Callers must set config.SetWindowsMirrorContext(repoPath) before calling when the repo needs Windows-specific path behavior for intent resolution.

func SortedUniqueStrings

func SortedUniqueStrings(values []string) []string

SortedUniqueStrings trims whitespace, drops empties + duplicates, and returns values sorted. Exported so commands/import_plugins.go can share the implementation instead of duplicating it.

Types

type AuditPrinter

type AuditPrinter interface {
	PrintAudit(w io.Writer, project, repoPath, agentsHome string)
}

AuditPrinter is implemented by platforms that render the per-platform audit block shown by `da status --audit`. Resolved in Phase 5 (Q2): the printer takes an io.Writer rather than a *ui.Sink — the platform layer only needs the ANSI colour constants from internal/ui (plain strings), so io.Writer keeps the dependency surface minimal while the lifecycle layer supplies os.Stdout at the call site.

type BranchSessionFinder

type BranchSessionFinder interface {
	FindSessionsOnBranch(home, projectPath, branch string, maxResults int) []BranchSessionInfo
}

BranchSessionFinder is implemented by platforms that embed git branch metadata in their session files, allowing orient to surface recent sessions on the current branch.

type BranchSessionInfo

type BranchSessionInfo struct {
	SessionID    string
	Timestamp    string
	MessageCount int
}

BranchSessionInfo holds brief info about a session that was active on a specific git branch. Used by BranchSessionFinder.

type BrokenLink struct {
	// PlatformID identifies which platform owns this link (e.g. "cursor").
	PlatformID string
	// LinkPath is the display-friendly path to the broken link — typically
	// repo-relative for project links or home-relative for user-config links.
	LinkPath string
	// Dest is the raw target string returned by the OS (os.Readlink output
	// for symlinks/junctions; empty for hard links whose canonical source
	// could not be matched).
	Dest string
	// DisplayDest is Dest after path resolution and ~/-prefix formatting,
	// suitable for direct rendering by doctor/status.
	DisplayDest string
}

BrokenLink describes a single managed link that exists but does not resolve to the expected canonical source. PlatformID is intentionally carried on the value so JSON reports can self-describe per-entry (Q1 in the proposal — kept).

func ScanSingleFileLinks(specs []SingleFileLinkSpec) []BrokenLink

ScanSingleFileLinks evaluates each spec and returns a BrokenLink for every entry that exists at LinkPath but does not satisfy any CanonicalPaths match. The rules mirror what cursor + the single-file managed paths in doctor.go already encode:

  1. If LinkPath does not exist on disk, nothing is reported — an absent link is "not present" rather than "broken".
  2. If LinkPath is hard-linked to ANY of CanonicalPaths, it is healthy and nothing is reported.
  3. If LinkPath is a resolvable managed link (POSIX symlink / Windows junction) and its target is missing, it is reported broken with the raw target in Dest.
  4. If LinkPath is a resolvable managed link and its target exists but does not match any canonical (mis-pointed), it is also reported broken — the canonical contract is "link matches a known source", not "link resolves to anything".

Note that platforms supply the PlatformID at the call site by post- processing the returned slice (this keeps the helper platform-agnostic and avoids threading platform identity through every spec).

func ScanSymlinkDir

func ScanSymlinkDir(dir string) (ok, broken int, brokenLinks []BrokenLink)

ScanSymlinkDir reads dir and classifies each entry as healthy or broken via the managed-link semantics used by status/doctor today. Returns the healthy count, the broken count, and the broken entries with raw + display target populated. Hidden files and subdirectories are skipped (the existing per-platform dirs are flat — rules/, agents/, skills/, agent/).

dir may not exist; in that case (0, 0, nil) is returned without error — "absent dir" is "not present" rather than "broken", matching the existing doctor/status behavior. Symlinks to directories (which on Windows show up as junctions and on POSIX as plain symlinks) are classified using managedLinkBroken so their broken state is reported even though they are not files.

type BrokenLinkReporter

type BrokenLinkReporter interface {
	BrokenLinks(project, repoPath, agentsHome string) []BrokenLink
}

BrokenLinkReporter is implemented by platforms that can enumerate their own broken project-scope links. Consumer: doctor.collectBrokenLinks.

type CanonicalBucket

type CanonicalBucket string
const (
	CanonicalBucketRules        CanonicalBucket = "rules"
	CanonicalBucketSettings     CanonicalBucket = "settings"
	CanonicalBucketMCP          CanonicalBucket = "mcp"
	CanonicalBucketSkills       CanonicalBucket = "skills"
	CanonicalBucketAgents       CanonicalBucket = "agents"
	CanonicalBucketHooks        CanonicalBucket = "hooks"
	CanonicalBucketCommands     CanonicalBucket = "commands"
	CanonicalBucketOutputStyles CanonicalBucket = "output-styles"
	CanonicalBucketIgnore       CanonicalBucket = "ignore"
	CanonicalBucketModes        CanonicalBucket = "modes"
	CanonicalBucketPlugins      CanonicalBucket = "plugins"
	CanonicalBucketThemes       CanonicalBucket = "themes"
	CanonicalBucketPrompts      CanonicalBucket = "prompts"
)

type CanonicalBucketSpec

type CanonicalBucketSpec struct {
	Name        CanonicalBucket
	Stage       int
	CountDirs   bool
	MarkerFile  string
	Description string
}

func CanonicalStoreBucketSpecs

func CanonicalStoreBucketSpecs() []CanonicalBucketSpec

func CanonicalStoreStage1BucketSpecs

func CanonicalStoreStage1BucketSpecs() []CanonicalBucketSpec

func CanonicalStoreStage2BucketSpecs

func CanonicalStoreStage2BucketSpecs() []CanonicalBucketSpec

type CommitAttribution

type CommitAttribution struct {
	CommitHash           string
	BranchName           string
	ScoredAt             string
	LinesAdded           int
	LinesDeleted         int
	ComposerLinesAdded   int
	ComposerLinesDeleted int
	HumanLinesAdded      int
	V2AIPercentage       float64
}

CommitAttribution holds AI vs human code attribution for a single commit. Sourced from Cursor's scored_commits table.

type DailyUsage

type DailyUsage struct {
	Date          string
	MessageCount  int
	SessionCount  int
	ToolCallCount int
}

DailyUsage holds activity counts for a single day.

type HookEmissionMode

type HookEmissionMode struct {
	Shape     HookShape
	Transport HookTransport
}

type HookPlatformOverride

type HookPlatformOverride struct {
	Event   string `yaml:"event"`
	Matcher string `yaml:"matcher"`
	File    string `yaml:"file"`
}

type HookShape

type HookShape string
const (
	HookShapeDirect       HookShape = "direct"
	HookShapeRenderSingle HookShape = "render_single"
	HookShapeRenderFanout HookShape = "render_fanout"
)

type HookSourceKind

type HookSourceKind string
const (
	HookSourceLegacyFile      HookSourceKind = "legacy_file"
	HookSourceCanonicalBundle HookSourceKind = "canonical_bundle"
)

type HookSpec

type HookSpec struct {
	Name         string
	Scope        string
	SourcePath   string
	SourceBucket string
	SourceKind   HookSourceKind
	Description  string
	When         string
	// WhenEvents, when non-empty, declares a multi-event hook. The
	// loader rejects manifests that set both `when` and `when_events`
	// (mutual exclusion) and manifests with duplicate or unknown
	// canonical events (P1c contract `when_events` rules). At render
	// time the spec is expanded into one rendered action per canonical
	// event the target platform documents.
	WhenEvents        []string
	MatchTools        []string
	MatchExpression   string
	Command           string
	TimeoutMS         int
	EnabledOn         []string
	RequiredOn        []string
	PlatformOverrides map[string]HookPlatformOverride
}

func ListHookSpecs

func ListHookSpecs(agentsHome, scope string) ([]HookSpec, error)

ListHookSpecs returns hook entries under ~/.agents/hooks/<scope>/: canonical bundles (…/<name>/HOOK.yaml) and legacy single-file JSON hooks. The hooks directory must exist; if it is missing, ReadDir fails with an error satisfying os.IsNotExist.

type HookTransport

type HookTransport string
const (
	HookTransportSymlink  HookTransport = "symlink"
	HookTransportHardlink HookTransport = "hardlink"
	HookTransportWrite    HookTransport = "write"
)

type LinkCounter

type LinkCounter interface {
	CountLinks(project, repoPath, agentsHome string) (ok, broken int)
}

LinkCounter is implemented by platforms that can count their healthy and broken managed links for a project. Consumer: doctor.countProjectLinks and the badge-math feeding status.

type MCPFileSpec

type MCPFileSpec struct {
	Scope      string
	BaseName   string
	SourcePath string
}

MCPFileSpec describes one canonical MCP config file under ~/.agents/mcp/<scope>/.

func ListCanonicalMCPFiles

func ListCanonicalMCPFiles(agentsHome, scope string) ([]MCPFileSpec, error)

ListCanonicalMCPFiles returns non-directory MCP config files under ~/.agents/mcp/<scope>/, sorted by basename. If the scope directory is missing, the error satisfies os.IsNotExist.

func ResolveCanonicalMCPFile

func ResolveCanonicalMCPFile(agentsHome, scope, name string) (*MCPFileSpec, error)

ResolveCanonicalMCPFile finds an MCP file by scope and name (basename or stem).

type ModelTokenUsage

type ModelTokenUsage struct {
	InputTokens              int
	OutputTokens             int
	CacheReadInputTokens     int
	CacheCreationInputTokens int
}

ModelTokenUsage holds cumulative token counts for a single model.

type OrphanCanonical

type OrphanCanonical struct {
	// Name is the entry basename as it appears under ~/.agents/<bucket>/.
	Name string
	// DisplayNote is either "" (plain orphan) or a pre-formatted suffix
	// describing the mis-pointed target. Callers concatenate it directly
	// when rendering.
	DisplayNote string
}

OrphanCanonical describes a canonical entry under ~/.agents/ that has no corresponding managed link in the project (orphan from the project's perspective). DisplayNote is empty for plain orphans; a mis-pointed link surfaces as a non-empty note formatted as " (mis-pointed: <target>)".

type OrphanCanonicalReporter

type OrphanCanonicalReporter interface {
	OrphanCanonicals(project, projectPath, agentsHome, bucket string) []OrphanCanonical
}

OrphanCanonicalReporter is implemented by platforms that maintain a canonical store under ~/.agents/<bucket>/ that can be inspected for orphans (entries with no matching project link). Consumer: doctor.collectOrphanCanonicals.

type Platform

type Platform interface {
	// ID returns the platform identifier (e.g. "cursor", "claude").
	ID() string
	// DisplayName returns the human-readable name.
	DisplayName() string
	// IsInstalled checks if this platform is installed on the system.
	IsInstalled() bool
	// Version returns the detected version string, or empty string.
	Version() string
	// CreateLinks creates all managed links for a project in repoPath.
	CreateLinks(project, repoPath string) error
	// RemoveLinks removes all managed links for a project from repoPath.
	RemoveLinks(project, repoPath string) error
	// HasDeprecatedFormat checks if the project has deprecated config files.
	HasDeprecatedFormat(repoPath string) bool
	// DeprecatedDetails returns a description of the deprecated format.
	DeprecatedDetails(repoPath string) string
	// SharedTargetIntents returns the ResourceIntents this platform would write
	// to shared (cross-platform) repo-local targets such as .agents/skills/*.
	// These intents are aggregated by the command layer into a single
	// ResourcePlan so compatible targets are deduped and conflicts are caught
	// before any filesystem writes occur.
	SharedTargetIntents(project string) ([]ResourceIntent, error)
}

Platform defines the interface all AI agent platforms must implement.

func All

func All() []Platform

All returns the ordered list of all supported platforms.

func ByID

func ByID(id string) Platform

ByID returns the platform with the given ID, or nil.

func Filter added in v0.4.1

func Filter(all []Platform, agentFilter string) []Platform

Filter returns the subset of all whose ID() matches agentFilter, in the original order. An empty agentFilter selects every platform (the unfiltered `da status`/`da doctor --verbose` audit). This is the single platform-selection primitive the lifecycle audit loop dispatches over, replacing the per-platform `agentFilter == "" || agentFilter == "<id>"` chain that previously lived in status.go's printAudit.

func InstalledEnabledPlatforms

func InstalledEnabledPlatforms(cfg *config.Config) []Platform

InstalledEnabledPlatforms returns platforms that are enabled in cfg and detected as installed on this machine. Order matches All().

func NewClaude

func NewClaude() Platform

func NewCodex

func NewCodex() Platform

func NewCopilot

func NewCopilot() Platform

func NewCursor

func NewCursor() Platform

func NewOpenCode

func NewOpenCode() Platform

type PlatformBadge

type PlatformBadge struct {
	// Name is the human-readable platform name shown in the badge row
	// (e.g. "Cursor", "Claude Code").
	Name string
	// Present is true when this platform has any managed state for the
	// inspected scope (project or user-home).
	Present bool
	// Broken is true when any of the platform's managed links are broken.
	Broken bool
}

PlatformBadge is the per-platform summary consumed by both the text (badge row) and JSON (statusJSONPlatform) status output. Per D5 of the proposal, this is the single source of truth that replaces the parallel `*TextBadge` and `collectProjectPlatforms` paths.

type PlatformUsageStats

type PlatformUsageStats struct {
	PlatformID        string
	TotalSessions     int
	TotalMessages     int
	TokensByModel     map[string]ModelTokenUsage
	DailyActivity     []DailyUsage
	RecentSessions    []SessionSummary
	CommitAttribution []CommitAttribution
}

PlatformUsageStats holds pre-aggregated usage data from a platform's native store. Used by StatsReader.ReadUsageStats.

type PluginKind

type PluginKind string
const (
	PluginKindNative  PluginKind = "native"
	PluginKindPackage PluginKind = "package"
)

type PluginMarketplace

type PluginMarketplace struct {
	Repo string   `yaml:"repo,omitempty"`
	Tags []string `yaml:"tags,omitempty"`
}

type PluginResources

type PluginResources struct {
	Agents   []string `yaml:"agents,omitempty"`
	Skills   []string `yaml:"skills,omitempty"`
	Commands []string `yaml:"commands,omitempty"`
	Hooks    []string `yaml:"hooks,omitempty"`
	MCP      []string `yaml:"mcp,omitempty"`
}

type PluginSpec

type PluginSpec struct {
	SchemaVersion     int                       `yaml:"schema_version"`
	Kind              PluginKind                `yaml:"kind"`
	Name              string                    `yaml:"name"`
	Version           string                    `yaml:"version,omitempty"`
	DisplayName       string                    `yaml:"display_name,omitempty"`
	Description       string                    `yaml:"description,omitempty"`
	Authors           []string                  `yaml:"authors,omitempty"`
	Homepage          string                    `yaml:"homepage,omitempty"`
	License           string                    `yaml:"license,omitempty"`
	Platforms         []string                  `yaml:"platforms"`
	Resources         PluginResources           `yaml:"resources,omitempty"`
	Marketplace       PluginMarketplace         `yaml:"marketplace,omitempty"`
	Dependencies      map[string]any            `yaml:"dependencies,omitempty"`
	PlatformOverrides map[string]map[string]any `yaml:"platform_overrides,omitempty"`
	Dir               string                    `yaml:"-"`
	ManifestPath      string                    `yaml:"-"`
	Scope             string                    `yaml:"-"`
}

PluginSpec is the canonical dot-agents plugin bundle manifest. Schema contract: schemas/plugin.schema.json - keep this struct aligned with the manifest contract and validate raw YAML bytes via PluginManifestSchema before trusting the typed fields.

func ListPluginSpecs

func ListPluginSpecs(agentsHome, scope string) ([]PluginSpec, error)

ListPluginSpecs returns canonical plugin specs for a scope. An empty scope scans all scopes under ~/.agents/plugins/.

func LoadPluginSpec

func LoadPluginSpec(pluginDir string) (PluginSpec, error)

LoadPluginSpec parses and validates a canonical plugin manifest from pluginDir.

type ResourceIntent

type ResourceIntent struct {
	IntentID      string                `json:"intent_id"`
	Project       string                `json:"project"`
	Bucket        string                `json:"bucket"`
	LogicalName   string                `json:"logical_name"`
	TargetPath    string                `json:"target_path"`
	Ownership     ResourceOwnership     `json:"ownership"`
	SourceRef     ResourceSourceRef     `json:"source_ref"`
	Shape         ResourceShape         `json:"shape"`
	Transport     ResourceTransport     `json:"transport"`
	Materializer  string                `json:"materializer"`
	ReplacePolicy ResourceReplacePolicy `json:"replace_policy"`
	PrunePolicy   ResourcePrunePolicy   `json:"prune_policy"`
	Provenance    ResourceProvenance    `json:"provenance"`
	Precedence    int                   `json:"precedence,omitempty"`
	ConflictKey   string                `json:"conflict_key,omitempty"`
	MarkerFiles   []string              `json:"marker_files,omitempty"`
	EnabledOn     []string              `json:"enabled_on,omitempty"`
	ReviewHint    string                `json:"review_hint,omitempty"`
}

func BuildSharedAgentFileSymlinkIntents

func BuildSharedAgentFileSymlinkIntents(project, targetRoot, destFileSuffix string) ([]ResourceIntent, error)

BuildSharedAgentFileSymlinkIntents builds symlink intents from each canonical AGENT.md file to a repo-local file path (OpenCode `.md`, Copilot `.agent.md`).

A missing canonical agents bucket (ENOENT) is treated as an empty resource set — projects without any agents yet are legitimate and should yield no intents, not a hard failure. Other errors (permission denied, IO) propagate so callers can surface them instead of silently producing an empty plan.

func BuildSharedAgentMirrorIntents

func BuildSharedAgentMirrorIntents(project string, targetRoots ...string) ([]ResourceIntent, error)

BuildSharedAgentMirrorIntents builds symlink intents for canonical agents/ buckets (per-entry directories with AGENT.md) into the given repo-relative target roots.

func BuildSharedCodexAgentTomlIntents

func BuildSharedCodexAgentTomlIntents(project string) ([]ResourceIntent, error)

BuildSharedCodexAgentTomlIntents builds render intents for `.codex/agents/*.toml` from canonical project agent directories.

A missing canonical agents bucket (ENOENT) is treated as an empty resource set — projects without any agents yet are legitimate and should yield no intents, not a hard failure. Other errors (permission denied, IO) propagate so callers can surface them instead of silently producing an empty plan.

func BuildSharedPluginBundleIntents

func BuildSharedPluginBundleIntents(project string, targetRoots ...string) ([]ResourceIntent, error)

BuildSharedPluginBundleIntents returns ResourceIntents for each canonical plugin bundle under ~/.agents/plugins/{scope}/ pointing at the given target roots. Each platform's SharedTargetIntents calls this with its own native plugin target path (e.g. OpenCode uses .opencode/plugins/, Cursor uses .cursor-plugin/, Claude uses .claude-plugin/, etc.). Platforms that do not yet have an emitter for their native plugin format simply omit this call from their SharedTargetIntents implementation — add it there when the emitter lands.

func BuildSharedSkillMirrorIntents

func BuildSharedSkillMirrorIntents(project string, targetRoots ...string) ([]ResourceIntent, error)

func (ResourceIntent) EffectiveConflictKey

func (i ResourceIntent) EffectiveConflictKey() string

func (ResourceIntent) Validate

func (i ResourceIntent) Validate() error

type ResourceOwnership

type ResourceOwnership string
const (
	ResourceOwnershipSharedRepo   ResourceOwnership = "shared_repo"
	ResourceOwnershipPlatformRepo ResourceOwnership = "platform_repo"
	ResourceOwnershipUserHome     ResourceOwnership = "user_home"
)

type ResourcePlan

type ResourcePlan struct {
	Resources []plannedResource
}

func BuildResourcePlan

func BuildResourcePlan(intents []ResourceIntent) (ResourcePlan, error)

func BuildSharedTargetPlan

func BuildSharedTargetPlan(project string, platforms []Platform) (ResourcePlan, error)

BuildSharedTargetPlan aggregates SharedTargetIntents from all provided platforms and builds a single merged ResourcePlan (dedupe, conflict detection). Dry-run and execute paths both use this so intent collection and planning happen once per operation.

func (ResourcePlan) Execute

func (p ResourcePlan) Execute(repoPath, agentsHome string) error

func (ResourcePlan) PruneStaleSharedTargets added in v0.4.0

func (p ResourcePlan) PruneStaleSharedTargets(repoPath, agentsHome string) ([]string, error)

PruneStaleSharedTargets deletes managed outputs that are no longer in the resolved plan (the EXACT/PRUNE projection, config-v2-coherence §7A.5). It scans only the parent directories that own at least one ResourcePruneTarget intent — the directories da actively projects into — and removes any entry there that (a) is not a wanted plan target and (b) is a managed link under agentsHome. User-authored files and links pointing outside agentsHome are never touched, so the prune cannot delete content da does not own.

Returns the list of pruned absolute paths and an aggregated error: a single stuck removal is reported (errors.Join) rather than short-circuiting, so one failure cannot hide the prune status of the rest and the caller never reports a converged tree while a stale managed output is still live.

func (ResourcePlan) RemoveSharedTargets

func (p ResourcePlan) RemoveSharedTargets(repoPath, agentsHome string) error

RemoveSharedTargets deletes managed outputs for each resource in the plan. Per-resource removal failures are aggregated (errors.Join) rather than short-circuiting so that one stuck target cannot hide the removal status of the rest, and so the caller (da remove) never reports a clean unlink while a managed output is still live on disk.

type ResourceProvenance

type ResourceProvenance struct {
	Emitter   string `json:"emitter,omitempty"`
	Operation string `json:"operation,omitempty"`
	Detail    string `json:"detail,omitempty"`
}

type ResourcePrunePolicy

type ResourcePrunePolicy string
const (
	ResourcePruneNone              ResourcePrunePolicy = "none"
	ResourcePruneTarget            ResourcePrunePolicy = "target_only"
	ResourcePruneGeneratedChildren ResourcePrunePolicy = "generated_children"
)

type ResourceReplacePolicy

type ResourceReplacePolicy string
const (
	ResourceReplaceNever                      ResourceReplacePolicy = "never"
	ResourceReplaceIfManaged                  ResourceReplacePolicy = "if_managed"
	ResourceReplaceAllowlistedImportedDirOnly ResourceReplacePolicy = "allowlisted_imported_dir_only"
)

type ResourceShape

type ResourceShape string
const (
	ResourceShapeDirectDir    ResourceShape = "direct_dir"
	ResourceShapeDirectFile   ResourceShape = "direct_file"
	ResourceShapeRenderSingle ResourceShape = "render_single"
	ResourceShapeRenderFanout ResourceShape = "render_fanout"
)

type ResourceSourceKind

type ResourceSourceKind string
const (
	ResourceSourceCanonicalFile   ResourceSourceKind = "canonical_file"
	ResourceSourceCanonicalDir    ResourceSourceKind = "canonical_dir"
	ResourceSourceCanonicalBundle ResourceSourceKind = "canonical_bundle"
)

type ResourceSourceRef

type ResourceSourceRef struct {
	Scope        string             `json:"scope"`
	Bucket       string             `json:"bucket"`
	RelativePath string             `json:"relative_path"`
	Kind         ResourceSourceKind `json:"kind"`
	Origin       string             `json:"origin,omitempty"`
}

func (ResourceSourceRef) CanonicalPath

func (r ResourceSourceRef) CanonicalPath(agentsHome string) string

func (ResourceSourceRef) Validate

func (r ResourceSourceRef) Validate() error

type ResourceTransport

type ResourceTransport string
const (
	ResourceTransportSymlink  ResourceTransport = "symlink"
	ResourceTransportHardlink ResourceTransport = "hardlink"
	ResourceTransportWrite    ResourceTransport = "write"
)

type RuleFileSpec

type RuleFileSpec struct {
	Scope      string
	BaseName   string // full file name, e.g. rules.mdc
	SourcePath string
}

RuleFileSpec describes one canonical rule file under ~/.agents/rules/<scope>/.

func ListCanonicalRuleFiles

func ListCanonicalRuleFiles(agentsHome, scope string) ([]RuleFileSpec, error)

ListCanonicalRuleFiles returns non-directory rule files under ~/.agents/rules/<scope>/, sorted by basename. Subdirectories are ignored. If the scope directory is missing, the error satisfies os.IsNotExist.

func ResolveCanonicalRuleFile

func ResolveCanonicalRuleFile(agentsHome, scope, name string) (*RuleFileSpec, error)

ResolveCanonicalRuleFile finds a rule file by scope and name. Name may be a full basename (e.g. rules.mdc) or a stem; stems try .mdc, .md, .txt in order.

type SessionReader

type SessionReader interface {
	// AIAgentPrefix returns the harness prefix used in the AI_AGENT env var
	// convention (e.g. "claude-code" for AI_AGENT=claude-code_2-1-138_agent).
	// Returns "" if this platform does not follow the AI_AGENT convention.
	AIAgentPrefix() string
	// SessionEnvs lists env var names (in preference order) that carry the
	// active session ID. First non-empty value wins.
	SessionEnvs() []string
	// EntrypointEnvs lists env var names for the session launch entrypoint.
	EntrypointEnvs() []string
	// ResolveModel scans the platform's session store for the model active in
	// the given session. Returns "" when unavailable or not yet implemented.
	ResolveModel(home, projectPath, sessionID string) string
}

SessionReader is implemented by platforms that expose structured runtime session data. It is the read-side complement to the write-side Platform interface and is kept separate to avoid burdening platforms that do not yet have confirmed session env var contracts.

Implement on a platform struct to make it participate in agent identity detection at `da workflow checkpoint` time. Stubs that return "" / nil are valid until the env var contract for that platform is confirmed.

type SessionSummary

type SessionSummary struct {
	ID        string
	Name      string
	UpdatedAt string
}

SessionSummary holds a brief description of a session.

type SessionTokenMetrics

type SessionTokenMetrics struct {
	InputTokens         int
	OutputTokens        int
	CacheReadTokens     int
	CacheCreationTokens int
	ReasoningTokens     int     // Codex o-series reasoning tokens; 0 for Claude Code
	CacheHitRate        float64 // CacheReadTokens/(CacheReadTokens+CacheCreationTokens); 0 = unavailable
	MessageCount        int
}

SessionTokenMetrics holds per-iteration token usage aggregated from a session's JSONL, time-windowed by checkpoint_at. Used by SessionTokenScanner.

type SessionTokenScanner

type SessionTokenScanner interface {
	ScanSessionTokens(home, projectPath, sessionID, afterTimestamp string) SessionTokenMetrics
}

SessionTokenScanner is implemented by platforms that support per-iteration token usage scanning from their session JSONL. Returns a zero SessionTokenMetrics when no matching entries exist or the session file is absent.

type SettingsFileSpec

type SettingsFileSpec struct {
	Scope      string
	BaseName   string
	SourcePath string
}

SettingsFileSpec describes one canonical settings file under ~/.agents/settings/<scope>/.

func ListCanonicalSettingsFiles

func ListCanonicalSettingsFiles(agentsHome, scope string) ([]SettingsFileSpec, error)

ListCanonicalSettingsFiles returns non-directory settings files under ~/.agents/settings/<scope>/.

func ResolveCanonicalSettingsFile

func ResolveCanonicalSettingsFile(agentsHome, scope, name string) (*SettingsFileSpec, error)

ResolveCanonicalSettingsFile finds a settings file by scope and name (basename or stem).

type SingleFileLinkSpec

type SingleFileLinkSpec struct {
	// LinkPath is the absolute path to the managed file link.
	LinkPath string
	// CanonicalPaths is the ordered list of acceptable sources. The first
	// hard-linked match wins; if none match and the link exists, the link
	// is reported as broken.
	CanonicalPaths []string
}

SingleFileLinkSpec describes one managed file link a platform owns: a fixed link path and the ordered list of canonical source files any of which would satisfy the link (the ordered list supports the .mdc → .md fallback already used by cursor rules).

type StatsReader

type StatsReader interface {
	ReadUsageStats(home string) *PlatformUsageStats
}

StatsReader is implemented by platforms that expose pre-aggregated usage statistics. ReadUsageStats returns nil when the platform's stats file is absent or not yet implemented.

type StatusBadger

type StatusBadger interface {
	Badge(project, repoPath, agentsHome string) PlatformBadge
}

StatusBadger is implemented by platforms that can produce a one-line status badge for the project. Consumer: status.collectProjectTextBadges and status.collectProjectPlatforms (text + JSON share the same value per D5).

type UserConfigReporter

type UserConfigReporter interface {
	// UserBrokenLinks returns broken managed links under the user's home
	// directory for this platform. Consumer: doctor.collectBrokenUserLinks.
	UserBrokenLinks(home string) []BrokenLink
	// UserBadge returns the user-config badge for the platform. Consumer:
	// status.collectUserConfigPlatforms.
	UserBadge(home string) PlatformBadge
}

UserConfigReporter is implemented by every platform that participates in the user-home diagnostics path. claude/codex/opencode/cursor report real managed user-home links; copilot implements it too but reports an empty/clean surface because its documented user-config layer is not yet wired by dot-agents (the user-scope wiring gap is tracked in PLATFORM_DIRS_DOCS). No platform lacks a user-config layer — the distinction is whether dot-agents wires it yet.

Jump to

Keyboard shortcuts

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