wiki-smith — keep the repo's .wiki/
A wiki is the repo's memory, written for agents: they find the rule here instead of asking again. The diary holds what happened; the topic page holds what holds now. A lesson that lives only in the diary is lost. wiki-smith decides which of the two a session needs, writes it the way this wiki's own rules say, and checks it.
Full method (file types, note format, ingest, linking, dates, commit): references/method.md. Config keys: references/config.md. Credits: ATTRIBUTION.md. Changes: CHANGELOG.md. Its own memory: .wiki/.
<skill> below is this skill's folder (for example ~/.claude/skills/wiki-smith). Every script takes -w <wiki dir> (default ./.wiki) and -c <config> (default <wiki>/wiki.config).
Step 0 — Find the wiki and its rules
- The wiki is
.wiki/at the repo root. If it has none, say so and offer to create one from templates/ (wiki.config.examplefirst); never create it unasked. - Read
.wiki/wiki.configif present (names: diary folder, inbox, index, footer label, date keys, main branch). Without it the defaults are generic English:journal/,inbox.md,index.md,Map:,## History,created/updated. - The wiki's own rules win. If the repo has a page on how to maintain it (
rg -n -i 'maintain|manter' .wiki -l) or anAGENTS.mdrule about the wiki, read that section and follow it where it differs from this skill. - If the user asked for "wiki update" and the wiki (usually the index) says to confirm first, do Step 1 and Step 2 as a plan only: one line per page with what changes, the titles of new notes, then ask "Apply?" and edit only after a yes. Otherwise act.
Step 1 — Look at what happened, then route
Read the session (and git diff, git log for the work done). Pick every row that applies, and nothing else:
| What the session had | What to do |
|---|---|
| A decision, delivery, change of course, incident or research | A daily note: scripts/note.sh -b "Title that says what changed" with the paragraph piped in. Then the day's line in the inbox. |
| A rule, lesson or fact that holds beyond today | Also fix the topic page: the rule as it holds now, no history (history stays in the note and in the page's History section). Correct what the page said before; never stack "it used to be". |
| A new subject with several facets (it will come back, and one page would mix kinds) | A map of content moc-<topic>.md plus pages <topic>-<facet>.md (cheatsheet, install, integration, comparison with the current tool...). See "New subject" below. |
| Nothing worth recording (only intermediate steps, or what a commit already says) | Say so in one line and create nothing. Padding the diary makes it unreadable. |
Only a fresh note is ever created for the diary; never rewrite another agent's note.
Step 2 — Write
Daily note. scripts/note.sh "<title>" creates <DIARY_DIR>/<today>/<HHMM>-<slug>.md in UTC and prints the path (slug rules and -2 on collision are in the script). Stdin is read only with -b: agent shells and CI leave stdin open forever, so without -b the script never waits for it. Pipe the body: printf '%s\n' "<paragraph>" | scripts/note.sh -b "<title>"; -b puts exactly one blank line between the title and the body. Without -b the file is just the title line; to add the paragraph afterwards start it with a blank line: printf '\n%s\n' "<paragraph>" >> "$path". The body is one well-written paragraph: what happened, why it matters, where the evidence is (commit, PR, issue), with a link to the topic page. Links are relative to the day folder (../../page.md). Template: templates/note.md.
First note of the day: scripts/day-index.sh creates the day's index; then add the day at the top of "last days" in the inbox. If the note changes how the repo works, also add one line to the inbox's highlights. The inbox never holds a note.
Topic page (ingest). Find it (rg -n -i '<word>' .wiki); if it does not exist and the subject will return, create it (template: templates/page.md); if it is small, put it in a section of a nearby page. Write the rule as it holds now. If a change alters what the page claims, add one line at the top of its History section. The note links to the page.
New subject.
- Write the pages first, one facet each, each self-contained from the first sentence. Typical facets: cheatsheet (the one to start with: addresses, commands, shortcuts), install, integration, comparison with the tool in use today.
- Create
moc-<topic>.md(templates/moc.md): an opening paragraph, "the pages" (one line each, cheatsheet first), "what to know first" (the few rules that bite). - Give every page the footer (
Map: [moc-<topic>](moc-<topic>.md) · Related: ...), add the map and each page to the index, and link the map from at least one other map. - A note in the diary says what was created and links the map.
Step 3 — Close (always, in this order)
scripts/dates.sh— thecreated/updatedfrontmatter, from git. Idempotent.scripts/day-index.sh— regenerate the day index. Never edit it by hand.python3 -I scripts/lint.py— fix everyfile:line: type: messageit prints until it exits 0 (exit 2 means problems). Do not ship a red lint.- Commit only your own files, by path, never
git add -A:scripts/sync.sh -m "wiki: <what>" <path>.... It commits in place when the checkout is on<main>, current and has no changes outside your paths; otherwise it carries your paths into a freshgit worktreefromorigin/<main>. Either way it lints exactly what it commits and refuses a red lint (exit 1, checkout untouched, carry worktree removed). In the carry the generated day index is never copied: it is regenerated in the worktree, so it lists only the notes that are in the commit. Add the attribution lines the repo or your host requires to the message.- Shared files (index, inbox) that another session may have edited too: do not pass them to a carry.
scripts/sync.sh -Wprints a fresh worktree fromorigin/<main>; make your edits there (the wiki is at the same relative path), rundates.sh,day-index.sh,lint.py, then runsync.sh -m ... [-p] <paths>from inside that worktree. It commits there, and with-prebases, pushes, fast-forwards the main checkout where it can, and removes the worktree. A carry that ships another session's lines (for example a link to a page that is only in the checkout) fails the lint and stops before anything is committed; that failure is the cue to use-W.
- Shared files (index, inbox) that another session may have edited too: do not pass them to a carry.
- Push only when the user asked to sync: the same call with
-p(fetch, rebase,push HEAD:<main>, fast-forward the checkout, remove the worktree). Never force, never touch other people's modified files: a file that differs from what you pushed and may hold another session's edits (index, inbox) is left as it is. If the checkout cannot be fast-forwarded (local<main>diverged, or overlapping local edits) it says so and still exits 0, because the push worked. Open a PR only when asked.
Commit what you wrote in the same session without waiting to be asked; publishing is the user's call.
Consulting the wiki
rg -n -i '<keyword>' .wiki (or skim the index), then read only the matching page or section, never the whole wiki. For what happened: the inbox (short), then only the days that matter; one day = its index and the notes it lists. If the repo has specs, they say what the system promises: rg -n -i '<keyword>' <specs dir>.
Pitfalls
- The diary is not the page. A rule written only in a note is lost; ingest it into the topic page in the same session.
- Rules without history. "Before creating a card, search for existing ones", not "we used to duplicate cards". The story stays in the note.
- One note per file. Several agents write the same day; they must never edit the same file. The day index is generated; conflicts there are solved by running
day-index.shagain. - Shared files across sessions. The day index,
index.mdand the inbox hold every session's uncommitted edits in one checkout.sync.shnever carries the day index (it regenerates it in the worktree) and lints before committing, but it cannot tell which hunks of a hand-editedindex.mdor inbox are yours: the carry takes the whole diff. When another session may have touched them, usesync.sh -Wand edit in the worktree. A foreign line that happens not to break the lint is still carried; that is the documented limit of the carry. - Never wait on stdin.
note.shreads the body only with-b; in an agent shell a bare read blocks forever. The same applies to any script you add. - Do not record the transient: a command that failed once and worked on retry, an environment glitch, a one-off task. Capture the working fix on the topic page, never "X is broken" (it hardens into a refusal after the cause is gone).
- Links: relative, first mention per section only; an anchor is GitHub's slug of the heading (lowercase, no punctuation, accents kept, space becomes hyphen). Renaming a heading breaks its anchors; the lint says so. Put a link example inside backticks so the lint does not read it.
- Rename or delete a page:
rg -l 'old-name' .wikiand fix every link, anchors included; delete only after the content moved. - Page size: past about 150 lines, or when rule, procedure, reference and history mix, split by kind.
- Private data stays out. Wiki pages are committed; keep secrets, tokens and personal data out, and reference where the secret lives instead.
- Lint limits: it does not catch contradictions between pages or stale claims; that is an agent's review when time allows.
day-index.shlimit: a day file with hand-written old-format notes is an error (file:line), not migrated; move each note into its own file withnote.sh, rerun.
Step 4 — Keep your own wiki
If this run taught something about running wiki-smith itself (a routing mistake, a script quirk), add one line to .wiki/lessons.md: - YYYY-MM-DD — <rule + why>. Edit the source clone (biliboss/wiki-smith) and reinstall (npx skills add biliboss/wiki-smith -g --copy -a claude-code -y), not the installed copy, which a reinstall overwrites. Design decisions go in .wiki/design.md.