Documentation
¶
Overview ¶
Package contexts manages one canonical global agent-context file (AGENTS.md per the agents.md spec) and projects it into each linked client's global context mechanism: a dedicated file in a rules directory, an @-import shim line, or a marker-delimited managed block. Per-project AGENTS.md files stay version-controlled in their repos and are out of scope. Every operation is a pure file operation; no running gateway is required.
Index ¶
- Constants
- Variables
- func HasFailures(results []SyncResult) bool
- func NeedsSync(statuses []ClientStatus) bool
- func SupportedSlugs() []string
- type ClientEntry
- type ClientStatus
- type LockFile
- type Manager
- func (m *Manager) Adopt(ctx context.Context, slug string) error
- func (m *Manager) CanonicalContent() (string, error)
- func (m *Manager) CanonicalPath() string
- func (m *Manager) Diff(ctx context.Context, slug string) (string, error)
- func (m *Manager) Dir() string
- func (m *Manager) HasCanonical() bool
- func (m *Manager) InitFromClient(slug string, force bool) error
- func (m *Manager) InitFromFile(path string, force bool) error
- func (m *Manager) InitFromTemplate(force bool) error
- func (m *Manager) SaveCanonical(content string) error
- func (m *Manager) Scan() []ScanEntry
- func (m *Manager) Statuses(ctx context.Context) ([]ClientStatus, error)
- func (m *Manager) SyncAll(ctx context.Context, opts SyncOptions) ([]SyncResult, error)
- func (m *Manager) SyncClient(ctx context.Context, slug string, opts SyncOptions) (SyncResult, error)
- func (m *Manager) Unsync(ctx context.Context, slug string) (UnsyncResult, error)
- func (m *Manager) UnsyncAll(ctx context.Context) ([]UnsyncResult, error)
- type ScanEntry
- type Strategy
- type SyncOptions
- type SyncResult
- type Target
- type UnsupportedClient
- type UnsyncResult
Constants ¶
const ( StateUnsupported = "unsupported" StateNeverSynced = "never-synced" StateInSync = "in-sync" StateStale = "stale" StateDrifted = "drifted" StateTargetMissing = "target-missing" )
Client sync states. "stale" means the target still matches what gridctl wrote but the canonical file has changed since (a sync is pending).
const ( ActionCreated = "created" ActionUpdated = "updated" ActionUnchanged = "unchanged" ActionSkippedDrift = "skipped-drift" ActionWouldCreate = "would-create" ActionWouldUpdate = "would-update" ActionError = "error" )
Sync result actions.
Variables ¶
var ( ErrNoCanonical = errors.New("no canonical context file; run 'gridctl ctx init' first") ErrCanonicalExists = errors.New("canonical context file already exists (use --force to overwrite)") ErrUnknownClient = errors.New("unknown client") ErrUnsupported = errors.New("client has no global context file mechanism") ErrNotAvailable = errors.New("client not initialized on this machine") ErrNotSynced = errors.New("client has never been synced") ErrOverCap = errors.New("rendered content exceeds the client's size limit") )
Sentinel errors callers branch on.
var ErrNewerLockVersion = errors.New("context lock file was written by a newer gridctl version")
ErrNewerLockVersion signals a lock file written by a newer gridctl.
Functions ¶
func HasFailures ¶
func HasFailures(results []SyncResult) bool
HasFailures reports whether any result needs the caller's attention: a write error or a drifted target that was skipped.
func NeedsSync ¶
func NeedsSync(statuses []ClientStatus) bool
NeedsSync reports whether any client requires attention: drifted, stale, or a recorded sync whose target file has gone missing. Backs `ctx sync --check` and the status exit code.
func SupportedSlugs ¶
func SupportedSlugs() []string
SupportedSlugs lists the supported client slugs, derived from the strategy table so error messages and help text never go stale.
Types ¶
type ClientEntry ¶
type ClientEntry struct {
Strategy string `yaml:"strategy"`
// Target is the absolute path gridctl wrote to.
Target string `yaml:"target"`
// InstalledHash is the managed-region hash exactly as written.
InstalledHash string `yaml:"installed_hash"`
// CanonicalHash is the canonical file's hash at sync time.
CanonicalHash string `yaml:"canonical_hash"`
// CreatedFile records whether gridctl created the target file itself
// (unsync then removes the whole file, not just the managed region).
CreatedFile bool `yaml:"created_file"`
SyncedAt time.Time `yaml:"synced_at"`
}
ClientEntry is one client's sync record.
type ClientStatus ¶
type ClientStatus struct {
Slug string `json:"slug"`
Name string `json:"name"`
Supported bool `json:"supported"`
Available bool `json:"available"`
Experimental bool `json:"experimental,omitempty"`
Strategy string `json:"strategy,omitempty"`
TargetPath string `json:"target_path,omitempty"`
State string `json:"state"`
Detail string `json:"detail,omitempty"`
SyncedAt *time.Time `json:"synced_at,omitempty"`
}
ClientStatus is one client's row in `ctx status` and GET /api/context.
type LockFile ¶
type LockFile struct {
Version int `yaml:"version"`
// Scope is "global" today. Present so a future project scope can
// share the schema without a format break.
Scope string `yaml:"scope"`
Clients map[string]*ClientEntry `yaml:"clients"`
}
LockFile records, per client, what gridctl last wrote and from which canonical revision. Drift is judged against InstalledHash; staleness against CanonicalHash.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns the canonical global context store under <home>/.gridctl/context and every projection into client files. All target paths resolve against home, so tests point it at a temp dir. Mutating operations serialize on mu so concurrent API requests never interleave lockfile read-modify-write cycles.
func NewManager ¶
NewManager builds a Manager rooted at the user's home directory.
func NewManagerWithHome ¶
NewManagerWithHome builds a Manager rooted at an explicit home directory. Tests use this to stay isolated from $HOME.
func (*Manager) Adopt ¶
Adopt pulls a target's managed content back into the canonical file (chezmoi re-add semantics), then re-syncs that client so its hashes return to in-sync. Other clients become stale, which is correct: the canon changed.
func (*Manager) CanonicalContent ¶
CanonicalContent reads the canonical file.
func (*Manager) CanonicalPath ¶
CanonicalPath returns the canonical global context file path.
func (*Manager) Diff ¶
Diff renders a unified diff between the canonical content and one client's current managed content.
func (*Manager) HasCanonical ¶
HasCanonical reports whether the canonical file exists.
func (*Manager) InitFromClient ¶
InitFromClient adopts a client's existing global context file as the canonical context. Content gridctl previously managed there (block, shim line, header chrome) is stripped so adoption never round-trips gridctl's own markers into the canon.
func (*Manager) InitFromFile ¶
InitFromFile adopts an arbitrary file as the canonical context.
func (*Manager) InitFromTemplate ¶
InitFromTemplate scaffolds the starter canonical file.
func (*Manager) SaveCanonical ¶
SaveCanonical writes the canonical file with a backup of the previous revision. Content is normalized to end with exactly one newline.
func (*Manager) Scan ¶
Scan inspects every supported client's import location so init can offer adoption before scaffolding.
func (*Manager) Statuses ¶
func (m *Manager) Statuses(ctx context.Context) ([]ClientStatus, error)
Statuses computes the per-client sync state for every known client, supported and unsupported, in display order.
func (*Manager) SyncAll ¶
func (m *Manager) SyncAll(ctx context.Context, opts SyncOptions) ([]SyncResult, error)
SyncAll projects the canonical context to every supported, available client. Unavailable clients are reported as skipped, never errors.
func (*Manager) SyncClient ¶
func (m *Manager) SyncClient(ctx context.Context, slug string, opts SyncOptions) (SyncResult, error)
SyncClient projects the canonical context to one explicitly named client. Unlike SyncAll, an unavailable client is an error here: the user asked for it by name and should hear why nothing happened.
type ScanEntry ¶
type ScanEntry struct {
Slug string `json:"slug"`
Name string `json:"name"`
Path string `json:"path"`
Exists bool `json:"exists"`
Size int64 `json:"size"`
}
ScanEntry reports what already exists at one client's likely global context location. Scanning never writes anything.
type Strategy ¶
type Strategy string
Strategy is how gridctl projects the canonical global context into one client's global context mechanism. Strategies are ordered by safety: a dedicated file gridctl fully owns, a single import line in a user-owned file, and a marker-delimited block in a shared file.
const ( // StrategyDedicatedFile writes a whole file gridctl owns inside a // rules directory the client reads (zero merge risk). StrategyDedicatedFile Strategy = "dedicated-file" // StrategyImportShim inserts one @-import line referencing the // canonical file; everything else in the target stays untouched. StrategyImportShim Strategy = "import-shim" // StrategyBlock writes the full file when the target is absent, or a // marker-delimited managed block when user content exists. StrategyBlock Strategy = "block" )
type SyncOptions ¶
type SyncOptions struct {
// Force overwrites drifted targets and repairs corrupt blocks.
Force bool
// DryRun renders and diffs without writing anything.
DryRun bool
}
SyncOptions configure a sync pass.
type SyncResult ¶
type SyncResult struct {
Slug string `json:"slug"`
Name string `json:"name"`
Strategy string `json:"strategy"`
TargetPath string `json:"target_path"`
Action string `json:"action"`
BackupPath string `json:"backup_path,omitempty"`
Diff string `json:"diff,omitempty"`
Error string `json:"error,omitempty"`
}
SyncResult describes what happened (or would happen) for one client.
type Target ¶
type Target struct {
Slug string
Name string
Strategy Strategy
// Paths is the write target per OS. A missing key means the client
// has no known global context path on that platform.
Paths map[string]string
// ImportPaths is where a user's pre-existing hand-written global
// context most likely lives, for `ctx init --import`. Defaults to
// Paths when empty.
ImportPaths map[string]string
// DetectDirs mark the client as initialized on this machine when any
// of them exists. Sync refuses to create client config trees
// wholesale, so these gate every write.
DetectDirs []string
// Frontmatter is prepended verbatim to dedicated files for clients
// whose rules format requires it (e.g. VS Code *.instructions.md).
Frontmatter string
// MaxChars caps the rendered target file size; 0 means unlimited.
// Windsurf enforces a 6,000-character limit on global_rules.md.
MaxChars int
// Experimental marks targets whose documented path rests on
// unofficial sourcing; surfaced in status output.
Experimental bool
}
Target describes one client's global context surface. Paths are ~-templates expanded against the Manager's home directory, keyed by GOOS ("darwin", "linux", "windows").
func FindTarget ¶
FindTarget returns the supported target for slug.
type UnsupportedClient ¶
UnsupportedClient is a linked client with no writable global context file mechanism. Surfaced honestly in status output instead of hacks.
func Unsupported ¶
func Unsupported() []UnsupportedClient
Unsupported returns linked clients with no file mechanism to sync to.
type UnsyncResult ¶
type UnsyncResult struct {
Slug string `json:"slug"`
TargetPath string `json:"target_path"`
// Action is "removed-file", "removed-region", or "already-gone".
Action string `json:"action"`
}
UnsyncResult describes the removal of one client's managed artifact.