| name | repo-wiki |
| description | Build and maintain a living, agent-first knowledge base for a git repo — a "repo-wiki" of architecture, decisions, constraints, product context, and glossary that stays trustworthy because staleness is detected by git and tacit knowledge is captured from chats. Use this skill whenever a project's docs are sprawling or out of date, when onboarding context lives only in people's heads, or when the user wants to "set up a knowledge base", "document the architecture", "write an ADR / decision record", "capture why we did X", "stop the docs from rotting", "keep docs in sync with the code", or "create a repo wiki". Also trigger when an agent needs durable project context across sessions, when CLAUDE.md / AGENTS.md has become a junk drawer, or after a substantial design discussion whose reasoning would otherwise be lost. Even a vague "our docs are a mess" or "how do we keep track of decisions" should pull this in.
|
| metadata | {"version":"0.1.0"} |
repo-wiki
A living, agent-first knowledge base that lives in a git repo at repo-wiki/.
It stays trustworthy because of two commitments: store only what the code can't
regenerate, and capture the reasoning that otherwise dies at the end of every
chat. Staleness is detected deterministically by git; tacit knowledge is mined
from conversations. Changes are applied directly — git review is the safety net — and
significant writes are reported so the human can review or revert. It's
apply-and-report, not ask-first.
Full design rationale: see docs/ideas/repo-wiki.md in this repo if present.
This SKILL.md is the operating manual; depth lives in references/.
When to use this skill
- Setting up a knowledge base for a repo → run
kb bootstrap, then follow the
scan → propose → agree → scaffold flow (see below and references/bootstrap.md).
- Capturing knowledge during/after a session — a decision settled, a constraint
learned, a gotcha hit. This is the highest-value, most frequent action.
- Checking freshness — which pages drifted from the code (
kb status).
- Catching up on chats the live hook missed (
kb catchup).
- Hollowing out a bloated
CLAUDE.md / AGENTS.md into a thin shim.
The two ideas that make this work
-
Recoverability. Knowledge that can be regenerated from the code (file tree,
API surface, "what the code does") is the fastest-rotting, lowest-value kind —
don't persist it; generate it on demand. Persist only the non-derivable:
the why, the gotchas, the mental models, the constraints, the product context.
(Exception: an expensive-and-hot derivation may be cached as a from-code
page that auto-refreshes — see references/pages.md.)
-
Chat is the primary intake. Most non-derivable knowledge is tacit — it
surfaces in conversation and is then thrown away. Code→docs is the cheap cache;
chat→knowledge is the precious, capture-or-lose-forever stream.
If you internalize nothing else: persist only the non-derivable, and mine the
chats.
Quickstart — three setup commands
Three composable commands cover setup; pick the one that fits:
| Command | What it does | When to use |
|---|
kb init | Install all hooks + plumbing only — no dirs or INDEX files | Any time; order-independent |
kb plumbing | Install hooks + gitignore only — no dirs | Any time; order-independent (same as init) |
kb scaffold | Create dirs + INDEX files only — no hooks; requires an explicit agreed-set flag | After Gate 1 (always — new or existing repo) |
The wiki structure is NEVER created automatically. It must be agreed with the user
first (via the scan → propose → agree → scaffold flow). kb init and kb plumbing
are plumbing-only; kb scaffold (with --recommended, --only, or --add) is what
creates the structure, and only after agreement.
init / plumbing — hooks only
python3 scripts/kb.py init
Installs all hooks (SessionStart, UserPromptSubmit/comments, PreCompact,
SessionEnd, post-commit) and gitignore entries. Creates repo-wiki/.ingest/.
No dirs or INDEX files are created. Idempotent. Run any time — before or after
kb scaffold, before or after Gate 1.
scaffold — structure only (requires agreed set)
python3 scripts/kb.py scaffold
python3 scripts/kb.py scaffold --recommended
python3 scripts/kb.py scaffold --recommended --add operations
python3 scripts/kb.py scaffold --only product,decisions --add operations
Creates repo-wiki/ + the agreed category folders + INDEX files. No hooks installed.
--recommended scaffolds the recommended CATEGORIES when the user accepted the default
set as-is. --only <comma-list> restricts the core set; --add <name> appends extra
categories (can be combined with either --recommended or --only). Idempotent: never
clobbers existing INDEX files. With no flags: creates nothing — prints the recommended
proposal + agree-first reminder instead (exit 0).
Use after Gate 1 agreement during the bootstrap flow to scaffold exactly the agreed
structure. Then read references/structure.md and seed the obvious pages. A new wiki
is small — INDEX.md plus a few real pages — not a tree of empty folders.
Bootstrapping — the universal flow (new or existing repo)
Structure must always be agreed before it is scaffolded — for every repo, new or
existing. The bootstrap flow is the entry point for creating a wiki. Run the read-only
scan first:
python3 scripts/kb.py bootstrap
Then follow references/bootstrap.md for the full interactive protocol. The flow is
multi-turn and cannot be compressed into a single command — it requires two human
gates:
- Gate 1 — Agree the MECE structure. Present the recommended category set as an
example proposal adapted to the repo's signals (ops indicators, ADR directory, etc.).
State explicitly that it is a proposal; let the user rename, add, or remove categories.
Agreement is recorded in the root
INDEX.md resolver. Only after Gate 1: run
kb scaffold --recommended (if accepted as-is) or kb scaffold --only/--add (if
adjusted) to create exactly the agreed folders. No mining begins until Gate 1 closes.
- Gate 2 — Agree the ingestion scope. For each source (chats, commits, docs, code),
show counts and estimated effort. The user picks which sources to mine and how far back.
Mining begins only after Gate 2 closes.
Once both gates close, mining fans out to parallel subagents — one per source type —
so a large repo's cold-start is fast and non-blocking. Generated pages are applied
directly and the batch is reported, so the human can review the diff and revert any
miss (apply-and-report applies end-to-end; the bootstrap import is itself a significant
write worth summarizing).
kb init / kb plumbing are order-independent. Run either at any time during the
bootstrap flow (even before Gate 1) to wire all hooks. Neither creates structure.
kb scaffold (with --recommended, --only, or --add) is what follows Gate 1.
Command comparison:
| kb init | kb plumbing | kb scaffold | kb bootstrap |
|---|
| What | Hooks + plumbing only | Hooks + plumbing only | Dirs + INDEX only | Read-only signal report |
| Creates structure? | No | No | Yes — after Gate 1 agreement | No |
| Gates | None | None | None | Leads into two interactive gates |
| Mining | None | None | None | Parallel subagents (after gates) |
| Output | Hooks + gitignore | Hooks + gitignore | Agreed folder tree | Proposed wiki pages |
| When | Any time (plumbing-only) | Any time (plumbing-only) | After Gate 1 | Any repo with history to mine |
Structure (recommended, not prescribed)
MECE at level 1, free below it. Everything is a folder. Every folder has a short
INDEX.md (purpose + local convention — never a file listing; that's what ls is
for).
repo-wiki/
INDEX.md # root: purpose + the MECE resolver + the manual
product/ # PROBLEM — who it's for, why, requirements, non-goals, metrics
glossary/ # LANGUAGE — ubiquitous language
architecture/ # SOLUTION — how it's built; from-code traversal caches
constraints/ # RULES — invariants, NFRs, gotchas (what must stay true)
decisions/ # RATIONALE — choices + rejected paths (NNNN-slug.md)
# optional: operations/ roadmap/ conventions/
inbox/ # raw captures awaiting triage
archive/ # superseded pages (retired, not deleted)
The resolver ("what is the primary subject?") and the disambiguation rules that
keep level-1 MECE live in references/structure.md. Read it before filing anything.
Page model — Compiled Truth + Timeline
Every page has a cheap mutable head and an append-only immutable evidence trail:
---
type: constraint
source: canonical # canonical | from-code | from-doc
covers: [src/billing/**] # paths this claims about → drift trigger
verified_against: a1b2c3d # sha at last confirmation
status: active # active | superseded | archived
---
## Compiled Truth ← current synthesis; rewritten on change; injected at session start
Billing endpoints must stay <50 req/s — the upstream ledger API rate-limits us.
## Timeline ← append-only provenance
- 2026-06-13 — captured from chat (session abc); 429s above ~50rps — verified @a1b2c3d
The rule that keeps trust intact: apply directly, then report significant writes —
git review is the safety net, so an agent writes the change rather than blocking to ask.
A Timeline append is cheap and silent; a new page or a rewritten Compiled Truth is
significant — name it in your turn so the human can review or git-revert. The source
axis still labels recoverability (from-code can be re-derived by re-reading code;
canonical is non-derivable why/intent), which drives freshness — but it no longer gates
the act of writing. Full page conventions and the three-way freshness model:
references/pages.md.
Capturing knowledge (the core loop)
When a session settles a decision, surfaces a constraint, or defines a term, file it.
Use the resolver to pick the folder, the page template (assets/templates/page.md)
for the shape, and apply the change directly — then report it if it's significant
(a new page or a rewritten Compiled Truth, not a one-line Timeline append). If code is
involved, stamp covers: with the relevant paths so the page enters the staleness
system already wired in.
The extraction prompts are the heart of this — references/extraction.md holds
two verbatim prompts: one mines a chat transcript for durable non-derivable
knowledge — decisions, constraints, terms, gotchas — even when the session changed no
code (a decision reached with zero diff is a first-class keeper); the other reconciles
a commit diff (refresh from-code caches + flag canonical drift) and needs no chat.
Both first load python3 scripts/kb.py outline
so they route and dedup against the actual wiki (real categories + existing pages),
not the generic defaults. Both are stingy and apply-and-report. Prompt 1 runs at session-end
and inside kb catchup; prompt 2 runs at commit/PR/CI.
Most capture is mechanical: the kb catchup engine mines past sessions so nothing
is lost even if you forget to file inline. See references/intake.md.
Write-back on a cache miss is the third capture path (alongside chat triage and
code-synthesis): when an agent needs project knowledge the wiki lacks, it resolves it
(reads the code, asks the user, or web-searches) and then adds a page so the next
agent doesn't re-derive it — only when the knowledge is durable and non-obvious. This is
why the shim and root INDEX.md carry a "missing knowledge?" instruction: the wiki is a
write-back cache that grows from use, not just from chats.
Find a page — kb search
Ranked full-text search over page bodies, for when ls + covers: + descriptive
filenames aren't enough (larger wikis, fuzzy recall):
python3 scripts/kb.py search "staleness covers"
python3 scripts/kb.py search "rotating tokens" -n 5
python3 scripts/kb.py search "edge auth" --json
It uses SQLite FTS5 / BM25 (porter stemmer, title-boosted), modeled on the
recall skill. The index is per-wiki — one .ingest/search.db scoped to this
repo's pages (gitignored, rebuilt locally by file mtime on every search, never
committed) — no embeddings, no models, stdlib sqlite3 only. Unlike recall's
machine-wide chat index, results never span repos. Each hit carries the page's Compiled-Truth
line as context, so a match comes with the page's gist, not just a snippet. On
interpreters whose sqlite3 lacks FTS5 it transparently falls back to ripgrep.
This same ranked search backs the web viewer's /api/search.
Local web viewer — kb serve
Browse the wiki in a browser without any external deps:
python3 scripts/kb.py serve
python3 scripts/kb.py serve --port 8080
python3 scripts/kb.py serve --wiki path/to/repo-wiki
Opens at http://127.0.0.1:<port>/. What you get: a sidebar tree, page render with
frontmatter table + Compiled Truth + Timeline, in-page TOC, ranked FTS5 search
(see kb search above), staleness pills (fresh / stale / unverified), covers
chips, and backlinks.
Design stance: stdlib-only server, vendored offline assets, localhost-only,
read-only. Editing pages via the browser is a deferred follow-up (see
references/web.md). Full technical details (API routes, security posture, asset
provenance): references/web.md.
Comments — human-in-the-loop feedback
The wiki viewer lets users highlight text and post inline comments. These land in
<wiki>/.comments/comments.jsonl and are surfaced to agents two ways:
- Passive hook (wired by
kb.py init as a UserPromptSubmit hook): injects open
comments at the top of every agent turn with zero poll overhead.
- Active watch loop: the agent calls
kb.py comments list --json --since <cursor>
on a short interval, acts on new comments, and advances the cursor.
Each comment carries a line anchor (page, line, end_line, section,
selected_text) so agents know exactly where in the file the feedback applies.
The agent-edits → viewer-updates loop: when an agent rewrites the .md file in
response to a comment, the viewer picks up the change within ~5 seconds via live
auto-refresh (per-page /api/changed poll + global /api/revision poll) — no manual
reload needed.
Consumption protocol: read the anchor, act (usually edit the .md directly —
the comment is the approval), append a Timeline entry, then
kb.py comments resolve <id> --note "...".
Full details — hook install snippet, watch-loop pseudocode, act-then-resolve rules,
edge cases: references/comments.md.
Staleness & catch-up
python3 scripts/kb.py bootstrap
python3 scripts/kb.py status
python3 scripts/kb.py status --new
python3 scripts/kb.py catchup
python3 scripts/kb.py verify <page>
python3 scripts/kb.py post-commit
python3 scripts/kb.py precompact
python3 scripts/kb.py session-end
python3 scripts/kb.py session-start
status is deterministic: it intersects each page's covers globs with what changed
in git since its verified_against sha. Stale = a review-queue entry, never a
commit gate (hard gates breed --no-verify habits). catchup wraps the vendored
recall scripts (multi-agent: Claude / Codex / pi) to enumerate and read sessions.
kb verify <page> is the one-command way to clear a stale/surfaced page: it bumps
verified_against to HEAD, appends a Timeline entry, and removes the page's surfaced
cursor key. After verify, status --new is silent for that page unless it drifts again.
status --new / delta mode: announces newly-stale pages exactly once per
fresh→stale transition. A .ingest/surfaced.json cursor tracks which pages have been
surfaced. Re-running status --new in the same session is silent. kb verify clears
the cursor entry, allowing the page to surface again if it later drifts.
Details and the watermark model: references/intake.md.
Keep heavy work off the main thread. Long transcripts, large diffs, and multi-session
catch-up are slow and token-heavy — dispatch extraction and catchup to subagents or a
background job, never inline in the user's session. The SessionStart hook is already
non-blocking by design: kb.py session-start does no git scan (it reports a cached drift
summary and spawns a detached kb.py reconcile to refresh it). See
references/activation.md.
Activation — wired triggers
Maintenance is an install problem, not a willpower problem. kb init or
kb plumbing (both plumbing-only, order-independent) installs four committed Claude
hooks and one git hook. After that, maintenance is automatic:
| Hook | Type | Fires | Effect |
|---|
| SessionStart | .claude/settings.json | every session | inject wiki, announce new-stale delta, surface inherited knowledge-debt, self-heal hooks, spawn reconcile |
| UserPromptSubmit | .claude/settings.json | every turn | tick the knowledge-debt counter (+ nudge) then inject open wiki comments |
| PreCompact | .claude/settings.json | before compaction | prints extract-before-compaction directive |
| SessionEnd | .claude/settings.json | session ends | nudge to run kb catchup |
| post-commit | .git/hooks/post-commit | every commit | diff-scoped staleness nudge + knowledge-debt tick (exit 0 always) |
Add a CI check on PRs as the ungameable backstop. Full details: references/activation.md.
Proactive capture — the knowledge-debt counter
Code drift is caught mechanically (kb status = git diff ∩ covers). Chat-borne
knowledge — decisions, gotchas, "why", new subsystems — had no equivalent signal: it
relied on the human remembering, or on edge-of-session nudges the agent blows past. A
standing "add a page" instruction decays over a long session as context fills.
The knowledge-debt counter is that missing recurring signal. In
repo-wiki/.ingest/state.json it tracks {commits, turns, since_sha, since_ts}:
- post-commit →
commits += 1; turn-tick (folded into the UserPromptSubmit
hook) → turns += 1.
- Crossing a threshold (≥ 5 commits or ≥ 15–20 turns since the last wiki write)
prints a visible, escalating, agent-directed line (
• → ⚠ → ⚠⚠) telling the
agent to proactively offer a one-line capture. post-commit also names committed
subsystems with no covering page.
- Self-resetting: writing any tracked page (
*.md, not INDEX.md/.ingest/
.comments) zeroes the counter — a diligent session is never nagged. The reset
deliberately ignores .ingest/ so the search.db cache and state.json itself
can't trigger a false reset.
Deterministic to fire (no LLM judgment), recurs during the session in front of the
agent, and never gates a commit — like kb status, a soft signal. Hard gates breed
--no-verify habits; this is capture pressure, not a blocker.
Upgrading an existing installation
No re-init required. The new logic rides existing hook invocations, so it activates
the moment a repo's kb.py is updated to this version:
- post-commit and SessionStart already call
kb.py; the new counter logic is
inside cmd_post_commit / cmd_session_start.
- The turn tick is folded into the UserPromptSubmit hook's command. Existing installs
pick it up via the SessionStart self-heal, which rewrites a repo-wiki hook whose
desired command changed (not just a stale path) — so the next session re-wires the
comments hook to also run
turn-tick. Zero manual steps.
state.json gains a knowledge_debt key, default-initialized on first read (old files
predate it; it's gitignored, so no migration conflict).
The only installs needing a manual kb plumbing are ones predating the UserPromptSubmit
comments hook entirely (self-heal repairs existing hooks but won't re-add a hook a user
may have intentionally removed).
New-collaborator / fresh clone
What a fresh clone inherits (from .claude/settings.json, committed):
- SessionStart hook — heartbeat, delta surfacing, knowledge-debt surfacing, self-heal, reconcile spawn
- UserPromptSubmit hook — knowledge-debt turn-tick + wiki comments injection
- PreCompact hook — extract-before-compaction directive
- SessionEnd hook — catch-up nudge
What it does NOT inherit:
.git/hooks/post-commit — git never clones local hooks; this is a hard git constraint
.ingest/ — local watermark state (gitignored by design; each developer has their own)
.comments/ — local comment store (gitignored)
The fix — zero extra steps needed:
The SessionStart hook self-heals the git hook automatically: on the first Claude Code
session after a fresh clone, kb session-start detects the missing
.git/hooks/post-commit and recreates it. No manual kb init is required.
One trust prompt is unavoidable. Claude Code will ask the user to approve the
SessionStart (and other) hooks on the first session. This is a Claude Code security
boundary that cannot be bypassed. After approving once, the hooks run automatically in
all subsequent sessions.
Summary: "one approval then automatic" is the target. Fully zero-touch is not
achievable (git-hook inheritance + trust prompt are hard constraints). If you want the
git hook before the first Claude session, run kb plumbing (or init) once after
cloning — i.e. python3 <path-to-skill>/scripts/kb.py plumbing (commonly
.claude/skills/repo-wiki/scripts/kb.py). It installs plumbing only and is idempotent.
The hook paths it writes are derived from the skill's actual install location, so they
work regardless of where the skill lives; re-running plumbing also repairs a hook whose
path went stale.
CLAUDE.md / AGENTS.md → thin shim
The instruction file mixes directives (do this) and knowledge (this is true).
repo-wiki absorbs the knowledge; the file shrinks to a ~12-line shim: a minimal
always-on pointer to the wiki plus the few universal/safety directives. Migrating an
existing file is the same triage primitive run over a file instead of a chat. See
references/claude-md-shim.md and assets/templates/shim.md.
Reference map
| File | Read it when |
|---|
references/extraction.md | the triage prompt — mining a chat + git diff into proposed pages |
references/structure.md | filing a page; setting up folders; the resolver + disambiguation |
references/pages.md | writing a page; frontmatter; the three-way freshness model |
references/intake.md | git + chat streams, watermarks, catchup, the vendored recall scripts |
references/activation.md | hooks, the SessionStart heartbeat, CI backstop, install discipline |
references/bootstrap.md | cold-starting a wiki in an existing repo — the 2-gate interactive flow |
references/claude-md-shim.md | migrating CLAUDE.md/AGENTS.md; what stays vs what moves |
references/web.md | kb serve — run instructions, API routes, architecture, security |
references/comments.md | wiki comments — passive hook, active watch loop, act-then-resolve consumption protocol |
Honest limit
This skill guarantees detection, reconciliation, and prompting. It cannot force a
human to author good knowledge — that judgment stays human. It surfaces the
opportunity at near-zero cost (a git-scoped change applied in place) and
makes ignoring it visible every session. Don't promise a fully-autonomous correct
KB; the human reviews the committed diff and reverts what's wrong — the report-and-revert
loop on significant writes is the check, not a pre-write gate.