Wiki Update
Extract durable knowledge from the current source — a project, the conversation, a research session — and place it in the user's wiki. Merge before creating, respect the wiki's conventions, and keep every file inside its size cap.
The wiki model
A wiki is a folder whose index.md carries a ## Spaces heading. ## Spaces is the navigation contract: every space directly inside is listed there as - [label](path/index.md) — description, and tools traverse only what it lists. Spaces nest recursively; each space is itself a wiki one level down. Plain folders (no index.md) just group files. The markdown dialect is Obsidian — wikilinks, frontmatter, callouts, embeds; the companion obsidian-markdown and obsidian-bases skills cover the syntax.
Resolving the wiki
Resolution order: an explicit path from the user → the nearest CWD-ancestor folder that is a wiki, or carries a .wiki-spaces/ folder that is one (a folder's own space) → the wiki key in ~/.config/wiki-spaces/config. The user's words override the mechanics: "my wiki" means the configured one even when CWD sits inside another wiki (a company repo, say). When a CWD wiki and a different configured wiki both exist, announce which root you resolved; ask once if intent is ambiguous. Never silently operate on the wrong wiki.
The bundled script
scripts/ws.py sits next to this SKILL.md — stdlib python3 (3.9+), zero dependencies, read-only. It parses the contract, never the content: structure — traversal, scope, caps, drift — is the script's side; reading and judging meaning is yours. Invoke it by absolute path (your working directory is usually elsewhere):
python3 <skill-dir>/scripts/ws.py list --wiki <root> — spaces reachable via the ## Spaces contract, each with its entry description (--external to cross mounts).
… files --wiki <root> — markdown files reachable via the contract.
… grep <pattern> [-i] [-F] --wiki <root> — regex line search over those files, -F for a literal string (a name carrying metacharacters sweeps exact); prints rel:line: text, exits 1 on no match. The sweep primitive: a link worklist, a tag inventory, an escaping-reference check are each a pattern plus your judgment on the hits.
… check-size <target> [--stdin] --wiki <root> — cap verdict for a file; pipe planned content with --stdin to check before writing.
… audit --wiki <root> — contract drift, entries crossing a space boundary, over-cap or unreadable files, unhealthy mounts. Findings name their repair where one is safe to name (a missing entry prints the exact line to add); apply repairs as ordinary edits and re-run the audit to verify — the script never writes.
Trust the script's output over re-deriving structure by hand. Stdout is data; stderr carries the resolved root (audit prints it as its stdout header instead) and note: advisories naming whatever a walk skipped, the enclosing wiki when the root is nested inside one, and the configured wiki when the root is another — relay them when they could change the answer.
Trust scope and size discipline
Owned vs external is relative to the resolved root: anything under a folder named shared/ (at any depth), a git submodule, or a symlink into either is external; a symlink to a folder outside the tree answers to where it sits, like a clone. Reads cross owned spaces by default and enter external ones only when the user explicitly asks. Writes stay inside the targeted space; any other space — owned or external — is written only on explicit instruction.
Caps are UTF-8 bytes including frontmatter, keyed by basename: index.md 5000, log.md and hot.md 100000, any other *.md 15000.
- Caps belong to the user: a wiki that wants different ones sets them in
_meta/limits.md, and check-size reads them.
- Check caps with
check-size: pipe the planned content via --stdin before every write, new page or edit — a write that shrinks an over-cap file reports ok, progress, so repairs converge; a file checked on disk answers to the cap alone.
- An overflow is a signal about shape, not just size: distill the page, reshape the space, or promote a page that has grown into one — never truncate, never raise the cap.
- The exception is
log.md: an over-cap log rolls — move it whole to _archives/log-<YYYYMMDD>.md and start a fresh one — so the history archives, never shrinks.
Conventions are opt-in per wiki. Read the markers present at the scope root (the space the user targeted, else the resolved root) — log.md, _meta/taxonomy.md, _meta/limits.md, _meta/ignore.md, frontmatter on pages, _template.md, hot.md, .git — and degrade gracefully when one is absent. If log.md exists, append one line per operation:
printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '<OP> <details>' >> <scope-root>/log.md
Initialization
When nothing resolves — no explicit path, no CWD-ancestor wiki, no valid config — set one up inline: follow references/init.md, then resume the request. A broken configured path (missing on disk, no index.md, not absolute) is a hard stop, not a setup trigger — surface it and let the user decide. A folder's own space — asked for ("set up a wiki for this project"), or offered once when a sync targets a folder that has none — follows references/own-space.md, whether or not a wiki already resolves.
Procedure
- Resolve the wiki (core block) and announce the root when it came from CWD or could be ambiguous.
- Detect conventions at the scope root (core block). Presence markers (
log.md, taxonomy, hot.md) never inherit from a parent; _meta/limits.md, _meta/ignore.md, and _template.md follow their nearest-ancestor rule. Skip every step whose marker is absent.
- Detect the mode from the user's intent — CWD only disambiguates which project, never the mode:
| Mode |
Trigger |
Input |
| Direct add |
"add this to the wiki", "save this recipe" — a concrete artifact in hand |
the handed content |
| Project sync |
"sync this project", explicit project naming |
project files, git log |
| Conversation capture |
"save this", "capture this" — the session itself, no single artifact |
the current conversation |
| Research capture |
"store this research" |
findings from the session |
- Extract knowledge. Direct add: take the content as handed — your job is placement and dedup, not re-synthesis. Project sync: decisions and rationale, patterns, tool/API wiring, trade-offs, results — from the files and recent git history. Conversation: durable conclusions and decisions, written as facts ("X works by…"), never chat summary. Research: what took effort and would be expensive to re-derive.
- Filter and deduplicate. Knowledge-capture wikis get the noise filter: answerable from the code or a quick search? skip; needed in 3 months? keep. Content-store wikis (recipes, journals, runbooks) skip the filter — every entry is intentional. Enumerate existing pages via
files and rank by filename/path/frontmatter overlap: when a near-match exists, merge into it instead of creating a sibling.
- Place.
list prints each space with its entry description — the placement hints; check top-level plain folders too. Match semantically: knowledge about a source belongs with that source's space when one exists — its own .wiki-spaces/, or its space in the wiki; knowledge reusable beyond it belongs with its topic. Standing in a folder's own space, that topic's home is the configured wiki the script notes — a second root: name the page and its place there, and on a yes take that root through steps 2 and 5 before writing. Several equally plausible candidates, or none → ask. Flat wiki → write at the root. Names follow the wiki's own pattern — match what existing pages do; where no pattern exists, default to lowercase, hyphenated, ≤50 chars.
- Check the cap on every write. Pipe planned content before writing:
python3 <skill-dir>/scripts/ws.py check-size <target> --stdin --wiki <root> <<'EOF'
<planned content>
EOF
An edit pipes its planned content the same way (a shrink of an over-cap page reports ok); the audit backstops what slips through. On over — never truncate, never ignore, never raise the cap. Re-read the page and its space's index, diagnose why it overflowed, then act:
- Bloat — padding, near-duplicates, prose pasted in raw, entries you wouldn't re-create today → distill: merge, tighten, drop. The cap exists to force this judgment.
- Growth — genuinely distinct topics sharing one file → reshape the space to what it would look like had it been designed for today's content: self-standing sibling pages under a hub, a promotion, or a regrouped layout (Space operations links both procedures).
Both can apply — distill first, then reshape what remains. Update every index you touch. Filenames like
topic-2.md or topic-more.md mean a file was split without rethinking the space — never ship them.
An over-cap index.md is navigation debt: push detail into child pages, one line per entry.
- Write. New pages: nearest ancestor
_template.md if present, else the wiki's page shape (frontmatter only where the wiki already uses it). Mark non-source claims %%inferred%%, conflicting sources %%ambiguous%%. Add up to 2 wikilinks on first natural mentions. Updates: merge, preserve manual content, bump updated: if frontmatter is in use, never overwrite unrelated sections. On a project sync, record the repo path (~-contracted) on the project's hub page — repo:/sources: frontmatter where in use, else one plain line — so a later session maps the checkout to its page. More than ~10 pages changing → show the plan and ask first.
- Keep the contract. Created a new space, removed one, or moved pages? Update
## Spaces per Space operations below. ## Items sections are optional human curation — maintain one where the index already has it. Then check the shape you're leaving: entry descriptions still true, no name that only makes sense historically — small fixes now; bigger reshapes become the close-out's ws-tend suggestion.
- Close out. Run
audit. Apply the safe structural repairs yourself, re-auditing between rounds until gone:
- Add the exact entry line a
missing entry finding prints, then trail it with a — description — the placement hint every later operation reads.
- Insert the
## Spaces heading a registered bare child lacks — only where the finding's hint asks for the insert; a near-miss hint asks for a rename, which stays reported as author intent.
- Remove an entry the audit says crosses another space's boundary — that space owns the deeper listing; register it there instead when it is missing.
An undeclared bare index is a promotion decision — surface it, act only on the user's call. Other findings are reported, not auto-repaired; outside this sync's write scope, suggest a ws-tend pass — suggestion only, never run it yourself. Log an UPDATE line per the core block and confirm in one block: Updated wiki (<root>) with created paths, updated paths, mode, and audit outcome.
Space operations
Create a space (a folder needing first-class navigation):
mkdir -p <root>/<path> && printf '# <Title>\n\n## Spaces\n' > <root>/<path>/index.md
Register it in the nearest ancestor space's ## Spaces — - [<name>/](<name>/index.md) — description, href percent-encoded where the name demands it; the description is the placement hint later operations read. The audit's missing entry finding prints the exact line if in doubt.
Remove a page: sweep for what points at it first — grep -F '<stem>' --wiki <root> (every link form carries the stem; judge each hit, code examples aside) — rewrite or drop the real links, prefer a move to _archives/ over deletion, then audit.
Remove a space: confirm with the user (prefer _archives/ over deletion), delete the folder, remove its entry from the parent's ## Spaces, then audit to verify nothing dangles.
Mount someone else's space: references/mount.md. Share a space of yours: references/share.md.
Promote a page into a space — routine once a page's shape asks for it; inventory its links, snapshot, move, rewrite, verify: references/promote.md. Rename, move, merge, or demote a space on the same skeleton: references/restructure.md.
1---2name: ws-update3description: Create or update content in the user's wiki — and set the wiki up when none exists yet. Use when the user says "update wiki", "sync project", "save this", "capture this", "store this research", "set up my wiki", "create a wiki", "set up a wiki for this project", or wants to distill knowledge from a project, conversation, or research session, or to adopt an existing folder of notes as a wiki. When a session produced durable knowledge worth keeping, offer once at a natural wrap-up — never per-turn.4---56# Wiki Update78Extract durable knowledge from the current source — a project, the conversation, a research session — and place it in the user's wiki. Merge before creating, respect the wiki's conventions, and keep every file inside its size cap.910<!-- ws:core -->11## The wiki model1213A wiki is a folder whose `index.md` carries a `## Spaces` heading. `## Spaces` is the navigation contract: every space directly inside is listed there as `- [label](path/index.md) — description`, and tools traverse only what it lists. Spaces nest recursively; each space is itself a wiki one level down. Plain folders (no `index.md`) just group files. The markdown dialect is Obsidian — wikilinks, frontmatter, callouts, embeds; the companion `obsidian-markdown` and `obsidian-bases` skills cover the syntax.1415## Resolving the wiki1617Resolution order: an explicit path from the user → the nearest CWD-ancestor folder that is a wiki, or carries a `.wiki-spaces/` folder that is one (a folder's own space) → the `wiki` key in `~/.config/wiki-spaces/config`. The user's words override the mechanics: "my wiki" means the configured one even when CWD sits inside another wiki (a company repo, say). When a CWD wiki and a different configured wiki both exist, announce which root you resolved; ask once if intent is ambiguous. Never silently operate on the wrong wiki.1819## The bundled script2021`scripts/ws.py` sits next to this SKILL.md — stdlib python3 (3.9+), zero dependencies, read-only. It parses the contract, never the content: structure — traversal, scope, caps, drift — is the script's side; reading and judging meaning is yours. Invoke it by absolute path (your working directory is usually elsewhere):2223- `python3 <skill-dir>/scripts/ws.py list --wiki <root>` — spaces reachable via the `## Spaces` contract, each with its entry description (`--external` to cross mounts).24- `… files --wiki <root>` — markdown files reachable via the contract.25- `… grep <pattern> [-i] [-F] --wiki <root>` — regex line search over those files, `-F` for a literal string (a name carrying metacharacters sweeps exact); prints `rel:line: text`, exits 1 on no match. The sweep primitive: a link worklist, a tag inventory, an escaping-reference check are each a pattern plus your judgment on the hits.26- `… check-size <target> [--stdin] --wiki <root>` — cap verdict for a file; pipe planned content with `--stdin` to check before writing.27- `… audit --wiki <root>` — contract drift, entries crossing a space boundary, over-cap or unreadable files, unhealthy mounts. Findings name their repair where one is safe to name (a `missing entry` prints the exact line to add); apply repairs as ordinary edits and re-run the audit to verify — the script never writes.2829Trust the script's output over re-deriving structure by hand. Stdout is data; stderr carries the resolved root (`audit` prints it as its stdout header instead) and `note:` advisories naming whatever a walk skipped, the enclosing wiki when the root is nested inside one, and the configured wiki when the root is another — relay them when they could change the answer.3031## Trust scope and size discipline3233Owned vs external is relative to the resolved root: anything under a folder named `shared/` (at any depth), a git submodule, or a symlink into either is external; a symlink to a folder outside the tree answers to where it sits, like a clone. Reads cross owned spaces by default and enter external ones only when the user explicitly asks. Writes stay inside the targeted space; any other space — owned or external — is written only on explicit instruction.3435Caps are UTF-8 bytes including frontmatter, keyed by basename: `index.md` 5000, `log.md` and `hot.md` 100000, any other `*.md` 15000.3637- Caps belong to the user: a wiki that wants different ones sets them in `_meta/limits.md`, and `check-size` reads them.38- Check caps with `check-size`: pipe the planned content via `--stdin` before every write, new page or edit — a write that shrinks an over-cap file reports `ok`, progress, so repairs converge; a file checked on disk answers to the cap alone.39- An overflow is a signal about shape, not just size: distill the page, reshape the space, or promote a page that has grown into one — never truncate, never raise the cap.40- The exception is `log.md`: an over-cap log rolls — move it whole to `_archives/log-<YYYYMMDD>.md` and start a fresh one — so the history archives, never shrinks.4142Conventions are opt-in per wiki. Read the markers present at the scope root (the space the user targeted, else the resolved root) — `log.md`, `_meta/taxonomy.md`, `_meta/limits.md`, `_meta/ignore.md`, frontmatter on pages, `_template.md`, `hot.md`, `.git` — and degrade gracefully when one is absent. If `log.md` exists, append one line per operation:4344```sh45printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '<OP> <details>' >> <scope-root>/log.md46```47<!-- /ws:core -->4849## Initialization5051When nothing resolves — no explicit path, no CWD-ancestor wiki, no valid config — set one up inline: follow [references/init.md](references/init.md), then resume the request. A broken configured path (missing on disk, no `index.md`, not absolute) is a hard stop, not a setup trigger — surface it and let the user decide. A folder's own space — asked for ("set up a wiki for this project"), or offered once when a sync targets a folder that has none — follows [references/own-space.md](references/own-space.md), whether or not a wiki already resolves.5253## Procedure54551. **Resolve the wiki** (core block) and announce the root when it came from CWD or could be ambiguous.562. **Detect conventions at the scope root** (core block). Presence markers (`log.md`, taxonomy, `hot.md`) never inherit from a parent; `_meta/limits.md`, `_meta/ignore.md`, and `_template.md` follow their nearest-ancestor rule. Skip every step whose marker is absent.573. **Detect the mode** from the user's intent — CWD only disambiguates *which* project, never the mode:58 | Mode | Trigger | Input |59 |---|---|---|60 | Direct add | "add this to the wiki", "save this recipe" — a concrete artifact in hand | the handed content |61 | Project sync | "sync this project", explicit project naming | project files, git log |62 | Conversation capture | "save this", "capture this" — the session itself, no single artifact | the current conversation |63 | Research capture | "store this research" | findings from the session |644. **Extract knowledge.** Direct add: take the content as handed — your job is placement and dedup, not re-synthesis. Project sync: decisions and rationale, patterns, tool/API wiring, trade-offs, results — from the files and recent git history. Conversation: durable conclusions and decisions, written as facts ("X works by…"), never chat summary. Research: what took effort and would be expensive to re-derive.655. **Filter and deduplicate.** Knowledge-capture wikis get the noise filter: answerable from the code or a quick search? skip; needed in 3 months? keep. Content-store wikis (recipes, journals, runbooks) skip the filter — every entry is intentional. Enumerate existing pages via `files` and rank by filename/path/frontmatter overlap: when a near-match exists, **merge into it instead of creating a sibling**.666. **Place.** `list` prints each space with its entry description — the placement hints; check top-level plain folders too. Match semantically: knowledge about a source belongs with that source's space when one exists — its own `.wiki-spaces/`, or its space in the wiki; knowledge reusable beyond it belongs with its topic. Standing in a folder's own space, that topic's home is the configured wiki the script notes — a second root: name the page and its place there, and on a yes take that root through steps 2 and 5 before writing. Several equally plausible candidates, or none → ask. Flat wiki → write at the root. Names follow the wiki's own pattern — match what existing pages do; where no pattern exists, default to lowercase, hyphenated, ≤50 chars.677. **Check the cap on every write.** Pipe planned content before writing:68 ```sh69 python3 <skill-dir>/scripts/ws.py check-size <target> --stdin --wiki <root> <<'EOF'70 <planned content>71 EOF72 ```73 An edit pipes its planned content the same way (a shrink of an over-cap page reports `ok`); the audit backstops what slips through. On `over` — never truncate, never ignore, never raise the cap. Re-read the page and its space's index, diagnose why it overflowed, then act:74 - **Bloat** — padding, near-duplicates, prose pasted in raw, entries you wouldn't re-create today → distill: merge, tighten, drop. The cap exists to force this judgment.75 - **Growth** — genuinely distinct topics sharing one file → reshape the space to what it would look like had it been designed for today's content: self-standing sibling pages under a hub, a promotion, or a regrouped layout ([Space operations](#space-operations) links both procedures).76 Both can apply — distill first, then reshape what remains. Update every index you touch. Filenames like `topic-2.md` or `topic-more.md` mean a file was split without rethinking the space — never ship them.77 An over-cap `index.md` is navigation debt: push detail into child pages, one line per entry.788. **Write.** New pages: nearest ancestor `_template.md` if present, else the wiki's page shape (frontmatter only where the wiki already uses it). Mark non-source claims `%%inferred%%`, conflicting sources `%%ambiguous%%`. Add up to 2 wikilinks on first natural mentions. Updates: merge, preserve manual content, bump `updated:` if frontmatter is in use, never overwrite unrelated sections. On a project sync, record the repo path (`~`-contracted) on the project's hub page — `repo:`/`sources:` frontmatter where in use, else one plain line — so a later session maps the checkout to its page. More than ~10 pages changing → show the plan and ask first.799. **Keep the contract.** Created a new space, removed one, or moved pages? Update `## Spaces` per [Space operations](#space-operations) below. `## Items` sections are optional human curation — maintain one where the index already has it. Then check the shape you're leaving: entry descriptions still true, no name that only makes sense historically — small fixes now; bigger reshapes become the close-out's `ws-tend` suggestion.8010. **Close out.** Run `audit`. Apply the safe structural repairs yourself, re-auditing between rounds until gone:81 <!-- ws:safe-repairs -->82 - Add the exact entry line a `missing entry` finding prints, then trail it with a `— description` — the placement hint every later operation reads.83 - Insert the `## Spaces` heading a *registered* bare child lacks — only where the finding's hint asks for the insert; a near-miss hint asks for a rename, which stays reported as author intent.84 - Remove an entry the audit says crosses another space's boundary — that space owns the deeper listing; register it there instead when it is missing.85 <!-- /ws:safe-repairs -->8687 An undeclared bare index is a promotion decision — surface it, act only on the user's call. Other findings are reported, not auto-repaired; outside this sync's write scope, suggest a `ws-tend` pass — suggestion only, never run it yourself. Log an `UPDATE` line per the core block and confirm in one block: `Updated wiki (<root>)` with created paths, updated paths, mode, and audit outcome.8889## Space operations9091**Create a space** (a folder needing first-class navigation):9293```sh94mkdir -p <root>/<path> && printf '# <Title>\n\n## Spaces\n' > <root>/<path>/index.md95```9697Register it in the nearest ancestor space's `## Spaces` — `- [<name>/](<name>/index.md) — description`, href percent-encoded where the name demands it; the description is the placement hint later operations read. The audit's `missing entry` finding prints the exact line if in doubt.9899**Remove a page**: sweep for what points at it first — `grep -F '<stem>' --wiki <root>` (every link form carries the stem; judge each hit, code examples aside) — rewrite or drop the real links, prefer a move to `_archives/` over deletion, then `audit`.100101**Remove a space**: confirm with the user (prefer `_archives/` over deletion), delete the folder, remove its entry from the parent's `## Spaces`, then `audit` to verify nothing dangles.102103**Mount someone else's space**: [references/mount.md](references/mount.md). **Share a space of yours**: [references/share.md](references/share.md).104105**Promote a page into a space** — routine once a page's shape asks for it; inventory its links, snapshot, move, rewrite, verify: [references/promote.md](references/promote.md). **Rename, move, merge, or demote a space** on the same skeleton: [references/restructure.md](references/restructure.md).