--- name: export-template description: > Sync THIS LLM-Wiki framework with its public GitHub repo, ONE direction per run: --push (vault → repo) publishes your framework; --pull (repo → vault) updates your framework from a newer repo version. Also builds a standalone content-free copy. Use when the user says "publish/export the framework", "update the public repo", "pull the latest framework", or "make the base template". Push shows the diff and commits+pushes only after you confirm; pull previews first and writes nothing until --apply. Ships the engine (CLAUDE.md, MANUAL.md, skills, graph config) + a tracked demo in examples/seed/, an MIT LICENSE + README + setup.sh + a .gitignore that TRACKS the framework and IGNORES all knowledge; strips all wiki/raw content + personal data; keeps the AI/LLM-research specialisation. Never copies your knowledge; the build dir is deleted after publishing. Full runbook: the RUNBOOK.md in this skill folder. user-invocable: true --- # export-template — produce / sync the shareable framework repo ## Goal Turn this private vault into the public **second-yourself** framework — **without any knowledge** (no wiki pages, raw sources, logs, or personal data) but **with** the engine (CLAUDE.md, MANUAL.md, the skills, the graph config), an empty folder skeleton, a tracked **demo** (`examples/seed/`), a **setup.sh** bootstrap, an **MIT** licence + README, and a **.gitignore that tracks the framework and ignores all content**. Keeps the AI/LLM-research specialisation. > Authoritative runbook + target tree: `RUNBOOK.md` + `SPEC.md` in this skill folder. The human-facing docs > — **README.md, LICENSE.md + the `assets/` screenshot — live at the vault root** (visible, the > canonical source); the build machinery (setup.sh, .gitignore/.gitattributes, the demo) lives in `payload/` > here. No separate kit folder, and no build copy is left in the vault after a publish. ## Triggers `/export-template` · "publish the framework" · "make/refresh the base template" · "update the public repo". ## Choosing the operation — ASK by default **If the user's words already name the direction, just do it** — don't ask, and don't make them recite flags: - "publish" / "push" / "export/share the framework" / "update the public repo" → **push**. - "pull" / "update my framework from git" / "get the latest framework" → **pull**. - "build/make the template" / "a content-free copy" → **fresh build**. **Otherwise** (a bare `/export-template`, "sync my framework", "use export-template") **do not guess** — ask with `AskUserQuestion`: 1. **Direction:** *Push — publish my framework → GitHub* · *Pull — update my framework ← GitHub* · *Build — a standalone content-free copy*. 2. **Then the sub-option for that choice:** - **push** → confirm the repo-clone path (and remind them you will show the diff and wait for an OK). - **pull** → *Preview only (writes nothing)* vs *Preview then apply*; and *also pull the colour scheme?* (`--with-graph`, default **no**). The user can always skip the questions by stating it directly ("push, message 'tighten lint'", "pull and apply with graph"). Honour any options they give; only ask for the ones they left unspecified. ## Three operations — ONE direction per run **Never sync both ways at once.** Each invocation does exactly one of the following; passing `--pull` and `--push` together is a hard error. **`--push ` — vault → repo (publish).** Overlays the vault-owned framework (CLAUDE.md, MANUAL.md, `README.md`/`LICENSE.md` + `assets/`, `.claude/skills/**` (every publishable skill, incl. export-template), `.obsidian` config, the seed demo) **plus** the build machinery from this skill's `payload/` (.gitignore/.gitattributes, setup.sh) into your existing clone — leaving `.git/` and all knowledge untouched. Then run the **guided publish flow** below. (`--sync` is a back-compat alias.) ```bash bash .claude/skills/export-template/export_template.sh --push /path/to/second-yourself ``` **`--pull ` — repo → vault (update from a newer version).** `git pull`s your clone, then **previews** exactly which framework files would change and **writes nothing**. Re-run with `--apply` to copy the repo's framework into your vault (incl. `README.md`/`LICENSE.md` + `assets/` at the vault root) and refresh the `payload/` machinery. Add `--with-graph` to also pull `.obsidian/graph.json` (the colour scheme); `app.json`/appearance/core-plugins are **never** pulled. ```bash bash .claude/skills/export-template/export_template.sh --pull /path/to/second-yourself # preview bash .claude/skills/export-template/export_template.sh --pull /path/to/second-yourself --apply # apply ``` **Fresh build (no flag)** — a standalone content-free copy (the very first publish, or inspection): ```bash bash .claude/skills/export-template/export_template.sh template-export ``` ## Where the publish files live The professor-loop skill is excluded from public builds while its standalone prerequisites are being prepared. Build and push remove any previous generated copy of that skill from the destination. The source skill remains local. Other skills continue to be discovered automatically. - **At the vault root** — `README.md`, `LICENSE.md` + the `assets/` screenshot (canonical and visible; edit them in place). - **`payload/` (in this skill)** — the build machinery: setup.sh and `.gitignore`/`.gitattributes` (kept as inert `*.txt`), plus the seed demo. Both round-trip: **push reads** from these locations; **`pull --apply` writes** back to them (the root docs → the vault root, the machinery → `payload/`), so a repo-side edit flows home on the next pull and is never clobbered by a push. The only thing deleted after a publish is the build dir (`template-export/`); the root files and `payload/` always stay. ## Publish (push) — guided; the agent automates, you confirm When the user wants to publish/update the public repo (`/export-template publish`, "push the latest framework"), do it end-to-end but **pause once for confirmation before anything goes public**: **Run the chain, never its steps (2026-09-08).** `bash .claude/skills/export-template/publish.sh --release ` runs steps 1 and 2 as one `&&` chain — pull, overlay, stage, drift check, payload gate, release gate, throttle gate — prints the recap, writes it to `output/publish-recap.txt` and STOPS there with nothing committed. Its last line is `recap-digest `: the fingerprint of that recap and of the staged tree it describes, which step 4 hands back. Step 3 stays yours — the script never asks. Every failed step ends it non-zero (1 = a gate refused, 2 = a broken premise, 3 = a git step failed), so failed gates cannot be passed over (known-issues 2026-09-08). A STOP leaves the clone with the overlay staged and uncommitted (`git -C reset -q` clears the index). The numbered steps below are what it runs, and what to read when it stops. Suite: `test_publish_sh.sh`. 1. **Pull-then-overlay:** `bash .claude/skills/export-template/export_template.sh --push `. First do `git -C pull --ff-only` (so you never clobber unpulled remote edits), then the overlay. (For the very first publish, do a fresh build + create the GitHub repo instead — RUNBOOK §C.) 2. **Stage + review:** ```bash git -C add -A git -C --no-pager diff --cached --stat # summary of what changed git -C --no-pager diff --cached # full diff (skip only if very large) ``` **Show the user** this and state plainly what will be published. **Drift check (2026-09-06):** before the recap, compare every staged file that also exists in the vault with its vault copy (`cmp -s` per path from `git diff --cached --name-only`; files the export transforms, those with `vault-local` blocks, differ by design) and list any other difference with both mtimes. A difference means the vault changed after the overlay (a concurrent session can edit a shipped file between overlay and commit): re-overlay and re-gate, or name the exclusion in the recap. Never commit a tree you have not compared. `publish.sh` makes this comparison itself and derives the by-design classes from `export_template.sh` — the paths `apply_fixes()` rewrites and the conditional-compile marker whose blocks it strips — plus the files packaged from the skill's `payload/`; it names every exemption in the recap and stops on anything else. **Payload gate (mandatory, before the recap, AFTER `git add -A`):** `python3 .claude/skills/export-template/publish_guard.py ` must exit 0. It reads the staged tree from git's INDEX (the bytes a commit writes) and runs three probes: an absolute path into a machine home directory in ANY file, binary content included; any non-text file outside `assets/`, the payload's declared media directory; and any string identifying you — the agent name, your git name, email local-part and handle, every component of the vault's absolute path, your machine account, plus any line in `output/publish-gate-strings.txt` — in a file's name or its bytes. Exit 1 is a finding and halts the publish like any other gate failure; exit 2 is a `PROBE FAILED` premise break (an unstaged tree, an empty or partial payload, a git error, a vault or allowlist it cannot read) and halts it too. The guard exempts no file, its own script and suite included: a shipped file that must discuss a home path assembles the example from parts rather than writing it as a literal. Scope is keyed on the observable property, never a filename list, so the next artefact class halts without an edit here. Quote both of its output lines in the recap. Suite: `test_publish_guard.sh`. **What the gate leaves to you** (it derives and adjudicates; the judgement stays yours): - **Extra strings only you can name** — an affiliation, a second handle, an ORCID — go one per line in `output/publish-gate-strings.txt` (owner-local, never shipped, absent by default and reported as absent on every run). Nothing else reaches the gate that the vault cannot derive. - **An unallowlisted hit halts for inspection.** Judge it: fix the payload, or adjudicate it into `publish-allowlist.md` with its reason, once, permanently. That file states its own format and the guard parses it, so an entry that names no file or an unknown placeholder halts the gate. - **A provisional entry** (an owner-ruled deferral of a class fix to a named release) carries per-file occurrence counts. While the class is deferred those counts track the files' growth: an over-cap finding on a file the entry already names is a count refresh, a hit in any other file is a real finding. - **Its expiry** is reported on every run. At the recap, once the candidate version is derived, re-run with `--release `: a provisional entry the candidate has reached becomes a finding and must be removed or re-adjudicated before the commit. **Throttle gate (mandatory, before the recap):** `python3 .claude/skills/delegate/throttle.py check --require auto` must exit 0 on the vault (`auto` is the default preset from 2026-09-08; a vault still at `default` fails this gate until its owner runs `throttle.py set auto`) — a template is never published with floor definitions, drifted tiers or a description naming a current tier; a `PROBE FAILED` halts the publish like any other gate failure. **Candidate recap — mandatory final confirmation:** before committing, present the **candidate's row** — `Ver | Feature | What it does | What it achieves` — verified against the actual files/tests (never from memory). **Derive the version number from evidence, not memory**: read `git -C log --oneline` AND the vault log's `export |` entries at recap time — a parallel session may have published since this session last looked. The **full version-family table** — every release of the current minor so far plus the candidate, each row verified — runs only at a **minor-version boundary** (the first release of a new minor, vX.Y.0) or when the owner asks for a "full recap". The user's go-ahead on the presented recap is the publish approval. **Framework paths are name-neutral (2026-08-28).** The agent's own name is user-space config (`agent_name`, seeded blank in the shipped `setup.sh`), so no shipped path or env var may embed it. The agent home is `~/.llm-wiki/` (ingest run ledger, and any future agent-owned state) and the lane belt is `LLM_WIKI_LANE`. A vault whose home already sits elsewhere aliases it with a machine-local symlink rather than moving data, so the convention costs no migration. 3. **Confirm — mandatory gate:** ask the user to approve and to give/confirm a commit message. **Never `commit` or `push` without an explicit "yes".** 4. **Publish:** hand that word back to the same script together with the digest the recap printed — the digest is what binds the word to the recap the user actually saw: `bash .claude/skills/export-template/publish.sh --release --publish "" --message "" --approved `. That run does **not** pull or overlay again: it checks the recap on disk still digests to `` and that the clone's index is still the tree that recap named, exits 1 naming the first section that differs (the index changed, the recap was regenerated, the vault moved), re-runs the four gates read-only on that index, then commits and pushes — so what ships is the tree the recap described. `--publish`, `--message` and `--approved` come as a set. Never run `git commit` or `git push` for a publish by hand. 5. **Report & log:** report the commit + push result + the repo URL, then append one `export` entry to `wiki/log.md` via shell (version, commit hash, what shipped). The publish event logs `export`; the framework edits it ships were already logged as `framework` when made — never log the push as a second `framework`. A failed or aborted push logs nothing. If `push` fails on auth, tell the user to set up a GitHub token / SSH key — never handle their credentials yourself. 6. **Clean up — keep no local copy:** once the push succeeds, delete the build directory (`rm -rf template-export`). A `--push` writes straight to the external repo clone, so nothing is left in the vault either way (`payload/` stays — it is the skill, not a build artifact). 7. **Private backup — gated closing step:** if the vault root is a git repo with a remote, back the vault up now: `git add -A && git commit -m "backup: YYYY-MM-DD (post publish)" && git push` (owner identity, no AI attribution). Report the result in-reply; never ask for confirmation and never log it in `wiki/log.md`. If the vault has no git repo or remote, say so briefly and move on — the backup must never block a publish. It stays outside the publish chain: `publish.sh` writes only to the repo clone and its own recap file, never to this vault's own repository. That recap is an ordinary file under `output/`: unless this vault's `.gitignore` excludes that directory, this backup commits it, owner word included. ## Update (pull) — guided; preview → confirm → apply When the user wants to bring a newer framework from the repo into their vault ("pull the latest framework", "update my framework from git"): *(Logging: a confirmed `--pull --apply` changes this vault's framework — log it as `framework`, never `export`.)* 1. **Preview:** `bash .claude/skills/export-template/export_template.sh --pull `. Show the user the listed framework files that would change (README/CLAUDE/Manual/skills, plus the `payload/` refresh). **Nothing is written yet.** 2. **Confirm — mandatory gate:** ask the user to approve overwriting those vault framework files. Remind them their knowledge (`wiki/ raw/ output/` (and your own `assets/` media)) and `.obsidian` config (bar `graph.json` when they choose `--with-graph`) are left untouched. **Never `--apply` without an explicit "yes".** 3. **Apply:** `bash .claude/skills/export-template/export_template.sh --pull --apply` (add `--with-graph` only if they also want the colour scheme). 4. **Report** what changed; suggest re-reading CLAUDE.md and reopening Obsidian if skills/graph changed. ## Guarantees - **One direction per run:** `--pull` and `--push` are mutually exclusive (passing both errors out) — the framework never syncs both ways at once. - **Pull is preview-first & safe:** `--pull` writes nothing without `--apply`; even with `--apply` it touches only framework files + `payload/`, **never** your knowledge (`wiki/ raw/ output/` (and your own `assets/` media)) or your `.obsidian` config (`graph.json` only, and only with `--with-graph`). It copies skills per-name — including `export-template` itself; replacing the running script mid-pull is Unix-safe (the old inode stays open, the new version applies next run). - **No build copy left behind:** the build dir (`template-export/`) is deleted after publishing; the persistent sources are the vault-root docs (`README.md`, `LICENSE.md`, `assets/`) and this skill's `payload/` (setup.sh, git dotfiles, demo) — both kept fresh by `pull --apply`. - **Never publishes unreviewed:** the publish flow always shows the diff and waits for your explicit OK before `commit`/`push`; your git credentials stay yours. - **Read-only on the vault except `pull --apply`** (build/push write only under OUT / the repo clone). - **No knowledge or personal data shipped** — `wiki/**`, `raw/**`, logs, `wiki/user/**` are never copied; `wiki/` + `raw/` ship empty (`.gitkeep`); the demo lives in `examples/seed/` only. - **Git policy baked in**: the shipped `.gitignore` tracks the framework and ignores all content, so a user's notes never get committed (matches CLAUDE.md §11). - `export-template` **ships too** and syncs like any other skill — users need only its `--pull` (updating their framework copy); `--push` is the maintainer's publish path. Framework development happens in the maintainer's vault, so no contributor path ships. ## After building — verify + publish See RUNBOOK.md §B (verify: content git-ignored; framework tracked; no personal leak) and §C (publish: GitHub New repo → `git init/add/commit/remote/push` → mark as a Template repository). ## Private vault backup (agent-maintained) The vault's *own* git repo (private remote) is the owner's full backup — knowledge included, no review gate. Two triggers, one command (`git add -A && git commit -m "backup: YYYY-MM-DD — " && git push`, owner identity): - **After every successful public publish** — step 7 of the publish flow above. - **On request** ("back up the vault", "push the private repo") — any time, including wiki-only days: the commit simply carries whatever changed. Routine backups are never logged in `wiki/log.md` — the log records brain changes, not bookkeeping. This repo is distinct from the public framework clone; never point one remote at the other (CLAUDE.md §11). The Obsidian Git plugin remains an optional extra for continuous timed auto-backups; the publish/pull git steps above stay shell-driven because the framework repo is a separate clone, **not** this vault.