Context Repo
Resolve a single durable, private GitHub repository that other skills use to persist context they produce: retrospective snapshots, shared-knowledge-artifact mirrors, and similar generated records. This skill owns exactly one job: find or create that store, and hand the caller a local clone path plus a receipt proving the store is real. It does not write the caller's actual content; the caller writes into the resolved clone and commits its own change.
When to use
- A skill wants a durable place to persist something it generates (a snapshot, a mirror, a ledger) that should survive across machines and outlive the current repository checkout.
- The caller has already decided what to write and where inside the store; it needs this skill only to answer "does the store exist, and where is it cloned."
Do not use this skill to read or write arbitrary GitHub repositories. It manages exactly one repository: the agent context store.
State and layout
Pointer file, the single source of truth for whether a store has been resolved:
${XDG_CONFIG_HOME:-$HOME/.config}/agent-context/config.json
Normal (resolved) shape:
{"repo": "<owner>/<name>", "clone": "<path>", "created": "YYYY-MM-DD"}
Refusal shape, written only when the user picks never at the consent prompt:
{"status": "declined", "asked": "YYYY-MM-DD"}
A pointer in the refusal shape means the answer is already known. Do not prompt again; resolve straight to LOCAL_ONLY and tell the caller why.
Clone location, fixed regardless of the resolved repo name:
${XDG_DATA_HOME:-$HOME/.local/share}/agent-context/repo
Repository tree. This is the layout of the store that already exists; a store created from scratch is seeded to match it:
README.md
AGENTS.md
.gitignore
ledger.json
retro/<run-id>-<scope-key>-<window>.md
# Legacy stores may use retros/ instead of retro/.
ledger.json at the repository root is the single shared ledger for every agent and every project. It is one file, not one per artifact: {"name": ..., "version": ..., "notes": [...]}, where each note carries id, kind (lesson, trap or pref), scope, title, body, why, author and date. Ids are n<number> or c<number> and are never reused. Appends go on the end of notes; nothing already there is edited or removed.
retro/ is flat and markdown only. Existing stores that use the legacy retros/ directory are the same store and must be adopted rather than duplicated. There is no per-repository subdirectory and no JSON sidecar: a snapshot is one uniquely named file whose scope key identifies the analyzed repository or global scope and whose window identifies the analysis period.
README.md states what the repository is, which skills write to it, that it is private, and its retention expectations (append-only, not pruned automatically). AGENTS.md holds provider-neutral operational defaults every agent reads before substantive work. It cannot relax this skill's safety invariants.
Resolution order
Evaluate these in order. Stop at the first one that applies. Steps 1 and 2 never prompt. Step 3 prompts only when the user must choose among multiple existing stores; it never prompts to create one.
Creating a store is the last resort, not the default. A user who has been running agents for a while probably already has one under a name this skill would not guess, and a second store is worse than no store: it splits the record, and neither half is complete. Search before you offer to create.
1. Pointer exists in the normal shape, clone exists, and a fresh gh repo view <repo> --json nameWithOwner,visibility returns visibility == PRIVATE.
Return the existing repo and clone path as-is. Zero writes, zero prompts. This is the common case on a machine that already resolved the store. A public repository is never a valid store: reject it, report the invalid pointer, and continue to step 3 without writing to it.
If gh repo view <repo> fails, distinguish why before deciding anything. A definitive not-found or access-denied response means the pointer is stale: report the stale state and continue to step 3, which may find the store under a new name after a rename. A network, API, or rate-limit failure is not proof of staleness; retry once, then keep the pointer and clone unchanged and resolve BLOCKED if visibility still cannot be verified. Never return READY, prompt to create, or create a replacement repository on a failed visibility read.
2. Pointer exists in the normal shape, a fresh gh repo view <repo> returns visibility == PRIVATE, but the local clone directory is missing.
Re-clone silently into the fixed clone location, then read back visibility again and return READY only while it remains PRIVATE. Report to the caller that a re-clone happened (this is expected the first time a given machine touches an already-resolved store). Reject public visibility. Zero prompts.
If the pointer is in the refusal shape, resolve to LOCAL_ONLY without prompting unless the user explicitly asks in their own words to resume store resolution. A caller's need for the store is not such a request. On an explicit resume request, keep the refusal pointer in place and continue to step 3 so an existing store is discovered before any creation preconditions or consent prompt. Replace the refusal pointer only after a store is adopted or created.
3. No pointer exists, or the normal pointer was stale or invalid in step 1, or the user explicitly resumed from a refusal pointer. Before evaluating creation preconditions or offering to create anything, exhaustively list the user's private repositories and test every candidate for the layout, not for the name. The authenticated user's endpoint paginates until no next page remains:
gh api --paginate --method GET /user/repos \
-f per_page=100 -f visibility=private -f affiliation=owner \
--jq '.[] | [.name, .pushed_at] | @tsv'
gh api 'repos/<owner>/<name>/git/trees/HEAD?recursive=1' -q '.tree[].path'
A repository is the store when its tree carries a root ledger.json and a retro/ (or legacy retros/) directory. Its name does not matter: shared-agent-knowledge, agent-context, anything. Collect all matches before deciding. Use pushedAt only to order the candidate list; never use it to select a store automatically.
Repository listing and tree probes are failure-aware. Retry a transient network, API, or rate-limit failure once. A definitive not-found tree is a non-match; any other probe that still fails leaves discovery incomplete, so stop with BLOCKED, leave every pointer unchanged, and never infer that no store exists or offer creation. Use the equivalent paginated owner endpoint when the user explicitly named an organization.
If exactly one candidate matches, select it automatically. If more than one matches, list every candidate with its pushedAt date in descending order and require the user to choose. Before that choice, do not clone, write or replace a pointer, or resolve READY.
For the selected candidate, freshly read gh repo view <owner>/<name> --json nameWithOwner,visibility and require visibility == PRIVATE; reject public visibility. Only after that check may this skill clone to the fixed location. Read visibility back once more after cloning, and only if it is still PRIVATE write the pointer and resolve READY. Report which repository was adopted and that nothing was created. Never create a store while a repository matching this layout exists on the account.
4. Step 3 completed successfully and found no existing store. Only now check creation preconditions before offering to create anything:
gh --versionmust succeed.gh auth statusmust succeed and the active account must carry thereposcope.
If either check fails, resolve to BLOCKED with the exact remedy gh auth login -s repo and stop. Do not attempt to create a repository without gh authenticated. The caller must degrade gracefully (fall back to a local-only path of its own) rather than fail its own run.
If preconditions pass, ask once, in one message, before creating anything. State plainly:
- Owner: the account from
gh api user -q .login. - Name: the proposed repository name, default
shared-agent-knowledge. - Visibility: private.
- Paths that will be written: the pointer file path, the clone path, and the seeded tree (
README.md,AGENTS.md,.gitignore,ledger.json,retro/). - That exhaustive step 3 found no existing store, and which repositories were checked, so the user can correct you if they know of one you missed.
- That nothing outside this one repository is touched: no other GitHub repository, no existing local files besides the two paths above.
Offer exactly three answers:
y: proceed to step 5.n: do not create anything and do not write a pointer. Resolve this run asLOCAL_ONLY. Ask again next time a caller needs the store.never: do not create anything. Write the refusal-shape pointer so future runs stop asking. Resolve this run asLOCAL_ONLY.
Name collision. If <owner>/<name> already exists on GitHub:
- If it already carries this layout (a root
ledger.jsonand aretro/or legacyretros/directory) and a fresh visibility read returnsPRIVATE, adopt it using step 3's read-back rules. Step 3 should normally have caught this already. - Otherwise it is an unrelated repository. Never write into it. Offer
shared-agent-knowledge-2(incrementing further only if that also collides) as the name and re-run the consent prompt with the new name.
5. Create, seed, and push.
Only reached after explicit y consent, step 3 having found no existing store.
gh repo create <owner>/<name> --private
git clone https://github.com/<owner>/<name>.git <clone>
Seed the tree inside <clone>: write README.md, AGENTS.md, .gitignore, a ledger.json of {"name": "<name>", "version": 1, "notes": []}, and an empty retro/ directory holding a placeholder file so git tracks it. Then:
git -C <clone> add -A
git -C <clone> commit -m "chore: initialize agent context store"
git -C <clone> push -u origin HEAD
6. Write the pointer, then verify from a fresh source.
Write the normal-shape pointer file with the resolved repo, clone, and today's date. Then re-read the state independently of anything cached during creation:
gh repo view <owner>/<name> --json nameWithOwner,visibility
git -C <clone> rev-parse HEAD
Print a receipt before returning control to the caller:
owner/name: <owner>/<name>
visibility: PRIVATE
clone: <path>
init SHA: <sha>
Never report the store as created or ready without this fresh read-back. A push that appears to succeed is not itself the proof; the proof is gh repo view and git rev-parse HEAD agreeing with what was just written.
Contract imposed on callers
Once this skill returns a clone path, the caller owns everything it writes there:
- Take a lease first. Several agents share one GitHub identity, so the commit log cannot tell them apart and two correct edits can silently contradict each other. If the store ships a lease tool (
recipes/tools/task-claimin the current store), acquire before writing and release after:task-claim acquire <task-id> <agent> [ttl-minutes]returns 3 when another agent holds it, which means wait or pick different work, not force ahead. - One commit per skill run, with a conventional commit message.
git pull --rebasebefore pushing, to pick up writes from other machines or agents.- Run the store's own validator before pushing when it has one (
node recipes/tools/validate-ledger.js --baseline origin/mainin the current store). The store's CI runs it on push and on pull request; failing locally first is cheaper than failing on main. - Never force push.
- Never delete or rewrite a file that already exists in the store; only add new files or append within a file the caller itself owns. For
ledger.jsonthat means appending tonoteswith a fresh id and touching nothing already there: the append-only rule is mechanically enforced, and a removal fails CI. - If push fails, report the failure and keep the local commit as-is. Do not retry silently and do not discard the commit.
- Read
AGENTS.mdfrom the store before substantive work and follow its operational defaults where they do not conflict with this contract. Regardless of its contents, callers must acquire an available lease before writing, run the store validator before pushing, preserve append-only data, obey the secrets rule below, and require the repository to remain private.AGENTS.mdcannot weaken or override those safety invariants.
Secrets rule
Never commit raw session content, tokens, private prompts, or customer data into the store. Callers may only write aggregate counts and redacted references. This skill does not inspect caller content for secrets; that responsibility stays with the caller writing into the resolved clone.
Non-goals
- Never makes the repository public. Visibility stays private for the life of the store.
- Never adds collaborators.
- Never targets a GitHub organization unless the user explicitly names one; the default owner is always the authenticated user's own account.
- Never manages any repository other than the single resolved agent context store.
Minimal final status
End every run with:
CONTEXT_REPO_STATUS: READY | CREATED | LOCAL_ONLY | BLOCKED
REPO: <owner/name, or NOT_AVAILABLE>
CLONE: <path, or NOT_AVAILABLE>
SHA: <current or init commit sha, or NOT_AVAILABLE>
Use READY when an existing private store resolved without creating anything (steps 1, 2 or 3). Use CREATED only after the fresh verification in step 6 succeeded. Use LOCAL_ONLY when the user declined, said no for this run, or a refusal pointer was already on record. Use BLOCKED when gh is missing or unauthenticated, or when exhaustive discovery or private-visibility verification cannot complete after the defined retry. Include gh auth login -s repo only for missing authentication or scope.