Documentation
¶
Overview ¶
Package surface holds the provider-surface registry: the verified facts about where each AI coding agent looks for a skill (its "skills home") and where it reads always-on context (CLAUDE.md, AGENTS.md, …), plus the detection and link logic that use those facts.
Why a registry, not a name check ¶
Every fact in this package was read from the provider's own current docs and is written down here with its SourceURL and VerifiedOn date, so it can be re-verified instead of recalled — the harness-discipline rule ("a provider fact is read, written down, and branched on by surface — never recalled") applied to a2a's own product code (spec 32 §2.1). Code that needs a provider fact asks a Surface row a question ("does this surface read AGENTS.md?"); it never branches on a provider id string inline. A new provider is a new row, not a new if-statement.
This package imports nothing of a2ahub's own; internal/cli is the (later) consumer.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrForeignLinkTarget is returned by Link when a surface's // <SkillsHome>/a2ahub entry already exists, is not a2ahub-owned (not a // symlink into the SSOT tree, not a stub carrying our marker tag), and // force is not set. Nothing is written. ErrForeignLinkTarget = errors.New("surface: link target is not an a2ahub-managed entry") )
Sentinel errors, one per failure class (internal/host idiom). Callers use errors.Is against these; a typed *Error carries the operation and offending input on top.
Functions ¶
This section is empty.
Types ¶
type Error ¶
type Error struct {
// Op names the failing operation (e.g. "Link").
Op string
// Input is the offending input, kept for diagnostics (may be empty).
Input string
// Err is the wrapped sentinel (see the vars above).
Err error
}
Error is the small typed error every exported operation in this package returns on failure. It always wraps one of the sentinels above so callers can use errors.Is/As; it never panics on bad input.
type LinkMode ¶
type LinkMode string
LinkMode records how a discovery entry was installed for a surface.
const ( // LinkSymlink means the entry is a relative symlink to the SSOT tree // (the preferred form — Claude Code documents symlinked skill // directories explicitly). LinkSymlink LinkMode = "symlink" // LinkStub means the entry is a thin stub SKILL.md pointing back at the // SSOT tree — the fallback used where symlinks are unavailable (e.g. // Windows without Developer Mode). The runtime fallback IS the // cross-platform mechanism; there is no build-tag branch for it. LinkStub LinkMode = "stub" )
type LinkResult ¶
type LinkResult struct {
// Surface is the row the entry was installed for.
Surface Surface
// Path is the repo-relative a2ahub entry, e.g. ".claude/skills/a2ahub".
Path string
// Mode is how the entry was installed.
Mode LinkMode
}
LinkResult describes one installed discovery entry.
func Link ¶
Link installs a discovery entry for surface s under root: <root>/<s.SkillsHome>/a2ahub, pointing at the installed SSOT tree at <root>/<ssotRel> (normally ".a2ahub/skill"). It first tries a relative symlink; if that fails (platform/permission error) it falls back to a stub SKILL.md carrying valid Agent-Skill frontmatter and a pointer to the SSOT tree's own SKILL.md.
Ownership probe (mirrors cmd_skill.go's skillTargetState/errSkillForeignTarget gate): an existing target is "ours" when its symlink destination resolves exactly to this repository's SSOT path, or a directory/file carries linkMarkerTag. A foreign target (present, not ours) is refused with ErrForeignLinkTarget and nothing is written, unless force is set. An absent target is always free to create. An authorized target (ours, or force over foreign) is removed first so the result mirrors — no orphaned stale entry.
type Surface ¶
type Surface struct {
// ID is the surface's stable identifier ("claude", "codex").
ID string
// SkillsHome is the repo-relative directory this surface's runtime scans
// for skills, e.g. ".claude/skills".
SkillsHome string
// ContextFile is the repo-relative file this surface reads as always-on
// context, e.g. "CLAUDE.md" or "AGENTS.md".
ContextFile string
// ReadsAgentsMD is true when this surface's runtime reads AGENTS.md as
// always-on context (Codex does; Claude Code does not — it reads
// CLAUDE.md instead, per code.claude.com/docs/en/memory).
ReadsAgentsMD bool
// SourceURL is the doc page this row was read from.
SourceURL string
// VerifiedOn is the date (YYYY-MM-DD) this row was last verified against
// SourceURL.
VerifiedOn string
}
Surface is one verified provider fact row: where that provider's runtime discovers a skill (SkillsHome) and where it reads always-on context (ContextFile), plus the provenance of the row itself (SourceURL, VerifiedOn) so it can be re-verified instead of recalled.
func Detect ¶
Detect returns every Registry() row whose marker directory (<root>/<Surface.MarkerDir()>, e.g. ".claude") exists under root, in Registry()'s deterministic order. Detection is best-effort: a missing root, a missing marker dir, or an unreadable entry all yield exclusion — Detect never returns an error, so a caller (e.g. `a2a init`) can always treat its result as "the surfaces this repo already shows".
func Registry ¶
func Registry() []Surface
Registry returns every known provider-surface row, in a fixed, deterministic order (claude, then codex). Adding a provider means adding a row here, verified against that provider's own current docs — see spec 32 §4 for the deliberately-out-of-scope list (Cursor, Gemini CLI, Copilot/VS Code, opencode, Amp).