Files
agent-lib/README.md
T
m3ta-chiron 7070d8cc25 feat(client): config search chain, --repo zero-config override, actionable missing-config error
sync/status now resolve the work repository from a fallback chain:
--repo flag > --config flag > AGENT_LIB_CONFIG env > user-level config
(XDG / AppData) > admin platform default. --repo works without any
config file; tokens fall back to AGENT_LIB_GIT_TOKEN/AGENT_LIB_GIT_USER
without ever overriding a configured token. A completely empty chain
yields an error listing every searched location, a minimal config
example and the --repo shortcut.

Closes beads: agent-lib-9o8
2026-08-23 15:08:27 +02:00

11 KiB

agent-lib

One Go binary, two roles: curator and client.

  • Curator sideagent-lib vendor … pulls external git repositories into a plain work repository under external/<source>/<type>/, pinned by revision, with per-source selection and collision-safe naming. Publication is nothing more than git commit && git push.
  • Client sideagent-lib sync pulls the work repository and deploys its content into the OpenCode runtime paths. Local modifications win over company updates, self-created items are never touched, and Windows toast notifications tell employees when their edits blocked an update.

No Nix, no Python, no git subprocess at runtime — a single static binary for windows/amd64, linux/amd64, linux/arm64, darwin/amd64 and darwin/arm64.

Installation

Download the binary for your platform from the GitHub releases, verify the checksum, and put it on your PATH:

curl -fsSLO https://github.com/m3tam3re/agent-lib/releases/latest/download/agent-lib_linux_amd64.tar.gz
tar xzf agent-lib_linux_amd64.tar.gz
sha256sum -c checksums.txt --ignore-missing
install -m 0755 agent-lib /usr/local/bin/
agent-lib version

Fleet-managed machines receive the binary via a pinned rollout — see docs/fleet-handoff.md. There is no self-update.

From source: go build . (version is baked in via ldflags; make build uses the git description).

Curator guide

The work repository layout:

skills/<id>/SKILL.md      own skills (folder artifacts)
commands/<id>.md          own OpenCode commands
agents/<id>.md            own OpenCode agents
mcp/<id>.yaml             own MCP fragments (never deployed by the binary)
external/<source>/<type>/ vendored content, mirrors the same four types
agent-lib.lock.json       lockfile v2 — pins every source

Vendoring a source

agent-lib vendor add superpowers https://github.com/obra/superpowers/tree/main/skills

Pasted GitHub/GitLab web-tree URLs are normalized automatically. A URL pointing directly at a type directory (e.g. …/tree/main/skills) selects that directory as the skills root. --ref <branch-or-tag> pins a ref; the resolved revision is pinned in the lockfile. Discovery understands the four standard directories (skills/, commands/, agents/, mcp/); skills are folders containing SKILL.md — found at any depth, so category-nested layouts (skills/engineering/tdd/SKILL.md) work too, with the folder's base name as the skill id — commands and agents are flat .md files, MCP fragments are .yaml. Everything is read-only scanned — upstream code is never executed.

Selection rules

  • Default: vendor everything (mode: all).
  • --include a,b,c switches the source to include mode: only those ids arrive, and new upstream items do not arrive automatically.
  • Hard errors, enforced by vendor add, vendor update and validate:
    • all combined with an include list → error
    • an exclude list without all → error (not supported in v2 selections)
    • an include entry that no longer exists upstream → error naming the item

Collisions and renames

Every item deploys into a flat per-type namespace. If a vendored id collides with one of your own items (or with another source's item), the operation aborts with a hard error naming both parties — before anything is written. Resolve it with an explicit, reviewable rename:

agent-lib vendor add superpowers "$URL" --rename review=superpowers-review

The on-disk folder always carries exactly the deployed name. Renaming onto another collision is an error.

The maintenance loop

agent-lib vendor diff superpowers   # read-only preview: new/changed/deleted vs selection
agent-lib vendor update superpowers # fetch latest of the pinned ref, rewrite, report
agent-lib vendor update --all       # every source, reported per source
agent-lib vendor remove superpowers # delete external area + lockfile entry

In all mode new upstream items arrive automatically (and appear in the update report); in include mode they deliberately do not — vendor diff surfaces them as available upstream, not selected. Every update re-runs the collision checks and honors the rename map.

Auditing

agent-lib validate                 # offline: rules + tree/lockfile consistency
agent-lib vendor list              # sources with pin, mode, item counts
agent-lib vendor inspect <name>    # full inventory with metadata + warnings

All three accept --json. The lockfile is deterministic JSON (sorted keys, fixed indentation) — identical reruns produce identical bytes, so git diffs stay clean.

Client guide

Employees run two commands:

agent-lib sync     # pull the work repository and deploy
agent-lib status   # report local state; exit 1 when updates are blocked

Both accept --repo <url> / --ref <ref> for config-free personal use — see Configuration below.

Deployment mapping (OpenCode target):

Type Destination
skills ~/.agents/skills/<id>/ (whole artifact folders)
commands ~/.config/opencode/commands/<id>.md
agents ~/.config/opencode/agents/<id>.md
mcp never deployed — stays under fleet-controller merge

Sync semantics (three-state)

Sync plans before it mutates. For every item the planner compares the repository, the local manifest and the on-disk content:

  • new item → deploy
  • managed & unmodified, upstream changed → update
  • managed & locally modified → skip + warn — your version stays
  • on disk but never deployed by us → leave + warn — self-created items are safe by construction
  • upstream deleted & unmodified → remove
  • upstream deleted & modified → skip + warn — your version stays

Sync is idempotent (a second run is a byte-level no-op) and fails cleanly: an unreachable repository never leaves a half-deployed machine.

Configuration

The repository to pull comes from the first source that provides one:

  1. --repo <url> flag (works with no config file at all)
  2. --config <path> flag
  3. AGENT_LIB_CONFIG env
  4. user-level config (personal machines)
  5. admin-protected platform default (fleet rollout)
Source Linux / macOS Windows
user-level config $XDG_CONFIG_HOME/agent-lib/config.json (default ~/.config/agent-lib/config.json) %AppData%\agent-lib\config.json
admin default /etc/agent-lib/config.json %ProgramData%\agent-lib\config.json

--repo overrides a repo_url from any discovered config; --ref narrows it to a branch or tag. When no config exists anywhere, the error lists every searched location, a minimal working example, and the --repo shortcut.

{
  "repo_url": "https://git.example.com/company/agent-content.git",
  "ref": "main",
  "token": "<read-only deploy token>",
  "token_user": "oauth2"
}

The token is used for basic auth only. It never appears in the lockfile, the manifest, logs or error messages. On --repo runs (or when the discovered config carries no token) AGENT_LIB_GIT_TOKEN / AGENT_LIB_GIT_USER supply credentials instead — env never overrides a configured token.

Personal quickstart (no fleet, no config):

agent-lib sync --repo https://github.com/you/your-agent-content.git

The work repository must be a committed git repository (sync pulls it read-only via go-git; local uncommitted state is ignored).

Notifications

On Windows, a toast fires when (and only when) a sync kept local modifications or discovered unmanaged items — never for routine syncs. The same warnings are appended to the sync log (~/.local/state/agent-lib/sync.log on Linux, %LocalAppData%\agent-lib on Windows) and surfaced by status. Linux/macOS notification is a no-op.

status --json is machine-readable: deployed revision, repository revision, pending changes, skipped and unrecognized items, and a problematic_drift flag that drives the non-zero exit code — suitable for headless drift checks.

Security / trust model

  • External sources are untrusted. Discovery and vendoring never execute upstream code; only file contents are read.
  • The shared read-only token limits blast radius; rotation is a single fleet run (see the handoff doc).
  • MCP fragments from vendored sources are never activated by the binary; merging and vault substitution remain an explicit fleet-controller decision.
  • Binary updates happen exclusively via pinned fleet rollouts — there is no self-update path to abuse.

Troubleshooting

Symptom Cause / fix
error: … no client config found. Searched (in order): … No config anywhere. Create one where the error suggests, or skip it: agent-lib sync --repo <url>.
error: … include entry "x" not found upstream Typo, or the item was renamed/removed upstream. Fix the lockfile include list, or re-add with a corrected --include.
error: … collides with own skills "x" Deployed-name collision. Add --rename <upstream>=<deployed> (add) or fix the rename map (update).
validate reports tree/lockfile divergence Someone edited external/ by hand. Re-run vendor update <source> to restore the pinned state.
Skill didn't update, skipped (local modifications kept): 1 Working as designed — you edited the deployed item. Restore the company version by deleting the local copy and re-syncing.
status exits 1 Local modifications block repository updates (or occupy names the repo needs). See status output for the item list.
Sync fails with clone/fetch error Work repository unreachable, or token expired. Previous local state remains intact; fix config/network and re-run.
No toast on Windows Toasts need an interactive user session — the scheduled task must run as the logged-in user, not SYSTEM.

Development

make build   # build with version ldflags
make test    # unit + black-box e2e (fully offline, local fixture git repos)
make lint    # go vet + gofmt check
./scripts/e2e-acceptance.sh   # full spec walk: 60+ assertions, offline

Release: goreleaser release on a tagged commit builds all five targets with checksums and publishes to GitHub releases. Verify with goreleaser release --snapshot --clean or a tagged --skip=publish dry run.

Scope and non-goals (v2)

In scope: vendoring with selection/renames into any git work repository; OpenCode-only deployment with three-state protection; toast notifications; goreleaser/GitHub release pipeline.

Not in scope: client-side selection or profiles, additional targets (Pi, Hermes, Crush, Amp), MCP merge/vault (fleet-controller concern), binary self-update, git submodule sources, migration tooling from the Python-era lockfile.