Documentation
¶
Overview ¶
Package projectsync provides shared helpers for creating project directory structures, restoring resource files, writing refresh markers, and managing gitignore entries. These were extracted from commands/add.go, commands/refresh.go, and commands/init.go so that multiple command implementations can share them without duplication.
Index ¶
- Constants
- func AdvancePromoteJournal(path, newState string) error
- func BeginPromoteJournal(agentsHome string, entry PromoteJournalEntry) (string, error)
- func CopyFile(src, dst string) error
- func CopyTree(src, dst string) error
- func CreateProjectDirs(project string) error
- func EnsureGitignoreEntry(repoPath, entry string)
- func ListBucket(scope string, spec BucketSpec) error
- func PromoteResource(name, projectPath string, spec PromoteSpec) error
- func ReadFrontmatterDescription(mdPath string) string
- func RecoverPendingPromote(agentsHome string, e PromoteJournalEntry) (string, error)
- func RefreshMarkerContent(version, commit, describe string) []byte
- func RemovePromoteJournal(path string) error
- func WriteRefreshToAgentsRC(projectName, projectPath, version, commit, describe string) error
- type BucketSpec
- type PromoteJournalEntry
- type PromoteSpec
Constants ¶
const ( PromoteStatePrepared = "prepared" PromoteStateCanonicalCopied = "canonical-copied" PromoteStateSourceRemoved = "source-removed" PromoteStateSymlinked = "symlinked" PromoteStateRCSaved = "rc-saved" PromoteStateRolledBack = "rolled-back" )
Promote journal states. The pipeline writes one entry, then advances through these states as each destructive step succeeds.
const PromoteJournalDir = ".promote-journal"
PromoteJournalDir is the relative directory (under ~/.agents/) holding promote-journal entries. Each in-flight promotion writes a single JSON file here; the file is removed once the promotion completes (or rollback finishes).
Variables ¶
This section is empty.
Functions ¶
func AdvancePromoteJournal ¶
AdvancePromoteJournal updates the State and UpdatedAt of an existing journal entry. The file is rewritten in place. A missing journal file is reported as an error so the caller can distinguish "lost-journal" failures from normal progression.
func BeginPromoteJournal ¶
func BeginPromoteJournal(agentsHome string, entry PromoteJournalEntry) (string, error)
BeginPromoteJournal writes a fresh journal entry under ~/.agents/.promote-journal/ with state="prepared". The returned path is the absolute journal file location; the caller passes it to AdvancePromoteJournal and RemovePromoteJournal as the promotion progresses.
The caller must populate Singular, Bucket, Name, SourcePath, and CanonicalPath on the entry. ID/State/StartedAt/UpdatedAt are overwritten.
func CopyTree ¶
CopyTree recursively copies the directory tree at src to dst, preserving file modes. Symlinks in the source tree are skipped — the canonical store holds only real files. On Windows a directory junction also carries os.ModeSymlink (Go 1.23+) and is skipped by the same check; a file hard link carries no reparse point and is indistinguishable from a real file by the OS, but production promote sources are real skill/agent trees with no internal hard links, so the symlink mode check is the complete and correct production contract on every OS.
func CreateProjectDirs ¶
CreateProjectDirs creates the standard per-project bucket directories inside AgentsHome. It is safe to call repeatedly; MkdirAll is idempotent.
func EnsureGitignoreEntry ¶
func EnsureGitignoreEntry(repoPath, entry string)
EnsureGitignoreEntry appends entry to <repoPath>/.gitignore if it is not already present. Silent no-op if the file cannot be opened or read.
func ListBucket ¶
func ListBucket(scope string, spec BucketSpec) error
ListBucket prints resources under ~/.agents/<bucket>/<scope>/ following the shared layout: each resource is a directory containing the spec's manifest. Description is parsed from the manifest's YAML frontmatter when present.
func PromoteResource ¶
func PromoteResource(name, projectPath string, spec PromoteSpec) error
PromoteResource promotes a repo-local resource (.agents/<bucket>/<name>/) into the shared agents store at ~/.agents/<bucket>/<project>/<name>/. The canonical location becomes the real directory and the repo-local path is replaced with a managed symlink pointing at it. Idempotent when the repo-local path is already a managed symlink to the canonical path.
func ReadFrontmatterDescription ¶
ReadFrontmatterDescription parses the YAML frontmatter of a markdown file and returns the value of the "description:" field. Returns "" if the file cannot be opened, has no frontmatter, or has no description field.
func RecoverPendingPromote ¶
func RecoverPendingPromote(agentsHome string, e PromoteJournalEntry) (string, error)
RecoverPendingPromote inspects a journal entry and either rolls it forward (when the next step is safe) or surfaces a manual-cleanup notice for states past the recoverable line. Returns a short human-readable description of what was done; the journal file is removed only when recovery completes cleanly.
State machine:
- "prepared": no destructive change yet — delete the journal.
- "canonical-copied": canonical exists but source intact; remove canonical.
- "source-removed": source gone, canonical present, no symlink yet — recreate symlink (preferred) so the on-disk layout matches the steady state of a successful promote.
- "symlinked": canonical + symlink ok; only rc.Save was pending — surface a manual rc update notice (we cannot guess the rc.Project field from here).
- "rc-saved": success — just delete the journal.
- "rolled-back": rollback already attempted — just delete.
func RefreshMarkerContent ¶
RefreshMarkerContent returns the byte content for a .agents-refresh marker file, suitable for os.WriteFile.
func RemovePromoteJournal ¶
RemovePromoteJournal deletes the journal file. Safe to call on a missing path — a not-exist error is treated as success.
func WriteRefreshToAgentsRC ¶
WriteRefreshToAgentsRC updates or creates .agentsrc.json with refresh metadata (version, commit, describe, refreshedAt) and removes a legacy .agents-refresh file if present.
Types ¶
type BucketSpec ¶
type BucketSpec struct {
// Bucket is the directory name under ~/.agents/, e.g. "agents" or "skills".
Bucket string
// ManifestName is the filename that must exist inside each resource directory
// to count as installed, e.g. "AGENT.md" or "SKILL.md".
ManifestName string
// Singular is the lowercase noun used in count output, e.g. "agent" or "skill".
Singular string
// Plural is the capitalized heading noun, e.g. "Agents" or "Skills".
Plural string
}
BucketSpec describes a per-resource-type bucket under ~/.agents/<bucket>/ so list/promote helpers can be shared between agents and skills (and any future resource types that follow the same shape).
type PromoteJournalEntry ¶
type PromoteJournalEntry struct {
ID string `json:"id"`
Singular string `json:"singular"`
Bucket string `json:"bucket"`
Name string `json:"name"`
SourcePath string `json:"source_path"`
CanonicalPath string `json:"canonical_path"`
State string `json:"state"`
StartedAt time.Time `json:"started_at"`
UpdatedAt time.Time `json:"updated_at"`
}
PromoteJournalEntry is the JSON shape persisted under ~/.agents/.promote-journal/. One entry exists per in-flight PromoteResource call; entries are removed on success or on completed rollback.
func ListPendingPromoteJournals ¶
func ListPendingPromoteJournals(agentsHome string) ([]PromoteJournalEntry, error)
ListPendingPromoteJournals returns all journal entries under ~/.agents/.promote-journal/ whose state is not terminal (rc-saved or rolled-back). The returned slice is sorted by StartedAt ascending so callers can process oldest-first during recovery.
type PromoteSpec ¶
type PromoteSpec struct {
BucketSpec
// Force overwrites an existing real canonical directory at
// ~/.agents/<bucket>/<project>/<name>/. When false, an existing real dir
// is fatal and the returned error appends ExistingRealDirHint as a
// recovery hint.
Force bool
// ExistingRealDirHint is the trailing recovery hint shown when canonical
// is a real directory and Force is false. Pass "; use --force to overwrite"
// for buckets that expose a force flag, or "; cannot promote" otherwise.
ExistingRealDirHint string
// RegisterInRC appends name to the bucket-specific slice in
// .agentsrc.json (e.g. rc.Agents or rc.Skills) and returns the new slice
// length, used in the SuccessBox count line.
RegisterInRC func(rc *config.AgentsRC, name string) int
// MirrorRefresh refreshes platform-level mirrors for the bucket. Errors
// must be surfaced via ui.Bullet by the callback itself; PromoteResource
// ignores the returned error so a failed mirror refresh does not roll
// back the manifest update (matching pre-extraction behavior).
MirrorRefresh func(projectName, projectPath string) error
}
PromoteSpec parameterizes PromoteResource for the agents/skills buckets. Add a new bucket by populating BucketSpec, wiring RegisterInRC and MirrorRefresh, and choosing whether the CLI exposes a force flag.