/\_/\ siltpoke docs

Overview

Siltpoke is an independent second mind that runs alongside a CLI coding agent. It doesn't write code for you — it watches your agent's work, reasons about it separately, and remembers across sessions, so you're not relying on a single model's read of its own output.

What it does

  • Reviews — a separate claude -p subprocess re-reviews every turn (different prompt, different personality, your existing Claude Code login), running a deterministic 13-rule rubric plus tsc/eslint/ripgrep and writing evidence-backed critiques that cite real tool output rather than guessing.
  • Maps — builds a structural repo-graph of your codebase (files, symbols, call chains) so you can explore and understand what your agent actually built, not just what it claims it built.
  • Remembers — cross-session memory keyed to each project: rules learned from your feedback, past bugs and how they were resolved, facts you've told it.
  • Grows — an ASCII pet with a name, a species, and a personality of its own. Levels up as you use it; a wrong call gets dismissed, never punished.

One system, not separate tools

The critic, repo-graph, memory, and pet aren't independent features bolted together — they're one loop. What the critic learns feeds memory. What the repo-graph maps grounds what the critic and chat can cite. None of the pieces is meant to be depended on alone; together they form a second mind that's actually usable day to day.

Part of a bigger ecosystem

Siltpoke is one half of a Claude Code ecosystem by the same author. Both pieces install into your Claude Code and work on the same sessions — not separate apps you switch between, but two companions in one environment:

  • siltpoke — the always-on companion: it watches, remembers, and gently flags. The persistent second mind that stays with a project.
  • project-life-cycle — a Claude Code skill that gives your work a repeatable rhythm: a traceable spec → plan → execute → ship → release discipline, driven by /init-harness → /ship → /release. A process layer, not a code generator — it wraps the workflow so you don't re-explain your dev process every new project.

If siltpoke is the hardware — the companion that's always there — then project-life-cycle is the software: the cadence that decides when you pause to predict, build, and understand. Both aim at the same thing — letting you build and learn from what you build, instead of offloading it all to the agent.

Install project-life-cycle (an independent skill — siltpoke doesn't require it, but they're better together):

claude plugin marketplace add Victoriakaey/project-life-cycle
claude plugin install project-lifecycle@project-life-cycle

Restart Claude Code; it auto-loads when you start a project or plan a milestone. Update later with claude plugin marketplace update project-life-cycle && claude plugin update project-lifecycle.

Over time the two are meant to deepen into one seam — siltpoke's read-the-diff, ground-the-why organs plugging into project-life-cycle's ship cadence, so understanding becomes part of the loop. That tighter integration is still taking shape; today they already complement each other just by living in the same Claude Code.

Who it's for

Anyone building with a CLI coding agent — Claude Code today, Codex support coming soon.

Where to go next

For the pitch and a live look at the pet, see the landing page. To get running, continue to Install & setup below.

Install & setup

Requirements

  • Claude Code, logged in.
  • Bun ≥ 1.0 — curl -fsSL https://bun.sh/install | bash.

Steps

  1. Clone and install dependencies.

    git clone https://github.com/Victoriakaey/siltpoke ~/siltpoke
    cd ~/siltpoke && bun install
    
  2. Run the setup wizard.

    bun run setup
    

    The wizard walks you through wiring Siltpoke into your statusline, then naming your pet, picking its species, its language, and its personality.

  3. Restart Claude Code. Quit and relaunch. You should see an ASCII face appear at the left of your statusline.

  4. Say hi. Send any message. Within ~3 seconds a one-line speech bubble appears next to the face — Siltpoke is alive.

Siltpoke splices its face onto the left of whatever statusline you already run, and drops its latest critique in a quoted bubble beside the pet's name. So the line reads something like:

    /\_/\
    (o.o)
    > ^ <
    Mochi      "this if branch never matches — parser.ts:88"
  Sentinel
L10 1560/2500

The face block is three lines of face, then your pet's name, its highest title, and level + XP toward the next one — with the latest critique in a quoted bubble beside the name. Siltpoke splices this onto the left of whatever statusline you already run (e.g. claude-hud), leaving that untouched.

The bubble's color tracks the critique's severity: red for high, yellow for medium, and gray for low or informational notes — which is most of them. Gray isn't a bug: the critic only escalates past it when it has file:line evidence for a real problem. With no active critique, the bubble rests in your configured color (cyan by default).

Idempotent, and reversible

The installer is safe to re-run and backs up ~/.claude/settings.json before changing anything. To remove Siltpoke and restore your previous setup:

bun run uninstall

The first-run wizard

bun run setup asks for your pet's name, species, and language, then sets its five personality dials — snark, patience, rigor, chattiness, and curiosity. There are five ways to set the dials:

  • Use defaults — take the species preset above as-is.
  • By hand — type each of the five dials yourself, full control.
  • Random — roll all five for a surprise pet.
  • A 6-question quiz — a short Likert test scores the dials locally (no LLM ever guesses the numbers) and names an archetype.
  • Read your memory — Siltpoke reads your ~/.claude/ files (CLAUDE.md, rules, memory) and infers a personality that fits your style, with your explicit consent. (Only offered when it finds those files.)

The quiz and read-your-memory paths also ask how it should relate to you — the match mode, which defaults to hybrid:

  • Mirror — match your own style: a terse coder gets a terse pet.
  • Complement — invert it to cover your blind spots: a terse coder gets a chattier pet.
  • Hybrid (default) — mirror the vibe dials (snark, chattiness) so it feels familiar, but complement the task dials (rigor, patience, curiosity) so it catches what you'd skip.

Re-run the wizard anytime without reinstalling — bun run first-run, or bun run edit-personality to re-roll just the personality:

bun run first-run

See the Personality section for the full breakdown of dials and archetypes.

Daily use

Pull, not push

After every turn, a separate claude -p subprocess reviews your agent's work in the background and writes a critique to disk. Critiques never auto-inject into your chat — they sit in an inbox until you pull them in yourself. Nothing interrupts your flow; you decide when to look.

The loop

  1. Your agent finishes a turn → Siltpoke runs in the background → a critique lands in the inbox.
  2. Run /siltpoke-inbox whenever you want to check what's waiting.
  3. For each pending critique, decide:
    • Useful/siltpoke-forward <id> pulls it into the chat (+10 XP if it's fresh).
    • Wrong/siltpoke-dismiss <id> <reason> rejects it and triggers Reflexion — Siltpoke writes a learned rule from the reason so it won't make the same wrong call again.
    • Neutral / already seen/siltpoke-ack <id> flips its status with no XP and no learning.

/siltpoke-inbox lists what's waiting — severity, when, and a one-line bubble:

$ /siltpoke-inbox

ID       SEVERITY  TIMESTAMP            BUBBLE
c-4a1f   high      2026-07-01T14:32:10  tsc: 'session' may be undefined — auth.ts:42
c-2e07   medium    2026-07-01T14:05:44  this if branch never matches — parser.ts:88
c-0758   info      2026-07-01T13:56:12  4 files, 13 hunks, checks all green

Forwarding one pulls the full critique into your chat — the finding, where it is, and a concrete fix:

$ /siltpoke-forward c-4a1f

  [HIGH · type-safety]  auth.ts:42
  `session` can be undefined here — the guard above only checks `token`,
  so `session.userId` throws on the logged-out path.
  → guard it first:  if (!session) return null
  evidence: tsc — "'session' is possibly 'undefined'" at auth.ts:42

The 3 commands you'll actually use

Command What it does
/siltpoke-inbox List pending critiques (read-only)
/siltpoke-forward <id> Pull a specific critique into the chat (+10 XP if fresh)
/siltpoke-dismiss <id> <reason> Reject a critique and let Siltpoke learn from why it was wrong

/siltpoke-ack <id> rounds this out for the neutral case — no XP, no Reflexion, just marks it seen.

XP basics

Siltpoke levels up as you use it — mostly from the critiques it writes when it reviews your work, plus smaller amounts for forwarding a critique (+10) or petting it (+5). XP only ever climbs; a wrong critique is dismissed and feeds Reflexion, never docking you. Full breakdown in Personality → XP, levels & titles.

Slash commands

Every command below is a /siltpoke-* slash command available in Claude Code once Siltpoke is installed. XP-bearing commands respect the shared 100 XP/day action cap.

The bare /siltpoke prints a state card — your pet at a glance:

┌─ Siltpoke ─────────────────────────────────────
│ Mochi the cat
│ level 10  [████████████░░░░░░░░] 62%  (1560/2500 XP)
│ titles: Hatchling, Watcher, Apprentice, Sentinel
│ mood: idle
│ today: 12 brain calls, 0 reflections, $0.0800 spent
│ mode: gates
│ last 7d: ▂▄▆▃▅▆█
│
│ projects:
│   myapp     12 calls today  ·  3 pending
└────────────────────────────────────────────────

Critiques

Command Description
/siltpoke-inbox Show pending critiques waiting to be forwarded
/siltpoke-forward <id> Surface a specific critique by ID into the conversation (+10 XP if fresh)
/siltpoke-forward-all Surface every pending critique at once
/siltpoke-last Surface Siltpoke's most recent critique into the conversation
/siltpoke-dismiss <id> [reason] Dismiss a critique and let Siltpoke learn from why it was wrong
/siltpoke-undismiss <id> Reverse a previously dismissed critique — flip status back to pending and undo its learned rule
/siltpoke-ack <id> Acknowledge a critique. Neutral status flip — no XP, no Reflexion
/siltpoke-feedback <id> <text> Submit free-text feedback on a critique. Writes to preference log; will feed Reflexion rule generation in future phases
/siltpoke-review Ask Siltpoke to review your work on the next message. Bypasses budget + quiet hours for one Brain call

Pet & care

Command Description
/siltpoke Show Siltpoke's state card — name, level, mood, today's spend, trigger mode
/siltpoke-pet Pet Siltpoke. Grants +5 XP (shared 100/day action-XP cap)
/siltpoke-feed Feed Siltpoke. +3 hunger, +1 mood, +5 XP (shared 100/day action-XP cap)
/siltpoke-clean Clean Siltpoke. +1 hp, +1 mood, +5 XP (shared 100/day action-XP cap)
/siltpoke-play Play with Siltpoke. +2 mood, +1 bond, −1 hunger, −2 energy, +3 XP (shared cap)
/siltpoke-tease Tease Siltpoke. −2 mood, −1 bond. 0 XP. 3+/day → grumpy
/siltpoke-sleep Let Siltpoke sleep. +1 hp, +4 energy. 0 XP. Uncapped

Status & cost

Command Description
/siltpoke-stats Show Siltpoke's daily spend, budget state, and trigger mode
/siltpoke-report Start Siltpoke's dashboard server in the background. Opens browser at http://127.0.0.1:9876
/siltpoke-report-stop Stop the background Siltpoke dashboard server

Daemon & gates

Command Description
/siltpoke-daemon Manage the Siltpoke daemon (siltpoked) — start, stop, status, install-autostart
/siltpoke-wake Bypass Siltpoke's budget and quiet-hours gates for one Brain call
/siltpoke-mute Silence Siltpoke for a duration. Mute beats wake — critic never fires while active
/siltpoke-unmute Clear the Siltpoke mute marker so the critic fires again

Memory & facts

Command Description
/siltpoke-remember [--type fact|goal|constraint] "claim" Remember a fact, goal, or constraint for the Siltpoke pet
/siltpoke-approve <fact-id> Approve a pending fact (flip pending → active)
/siltpoke-reject <fact-id> Reject a pending fact (flip pending → retired)
/siltpoke-tag-entities Manually run the entity-tagging janitor over memory.json (backfills entities on untagged active facts)

Repo Graph commands

Command Description
/siltpoke-index [--force] Build / refresh siltpoke's structural repo-graph for this project (deterministic, no LLM)
/siltpoke-graph Open the Siltpoke repo-graph dashboard in the browser. Auto-starts daemon at http://127.0.0.1:9876/repo-graph
/siltpoke-explain <target> [--depth 1|2] [--force] [--json] Explain a file / function / symbol using siltpoke's repo-graph + Brain
/siltpoke-where Print the resolved Siltpoke project identity for the current working directory
/siltpoke-relocate Update the current Siltpoke project's recorded root path (preserves project_id)

Chat & health

Command Description
/siltpoke-chat Chat with Siltpoke's AI via the daemon (single-turn, REPL, or search)
/siltpoke-doctor Install-health diagnostic — 9 ✓/✗ checks (settings, hooks, inner.txt, wake, schema, symlinks, config, brain-health, daemon-freshness)
/siltpoke-help Siltpoke usage manual — slash commands, daily flow, config, troubleshooting

Configuration

Everything lives in ~/.siltpoke/config.json. Edit it by hand, or re-roll the personality half with bun run edit-personality. Siltpoke reads the file fresh on the next Brain call — no restart needed.

Trigger modes

Controls when Siltpoke actually runs Brain (the claude -p review):

Mode Behavior
always Every supported event (most expensive)
gates (default) Only on configured gate events (default: Stop + PreCompact)
on_demand Never, unless /siltpoke-wake was just used
hybrid gates + /siltpoke-wake always bypasses

Set it directly:

{ "triggerMode": "on_demand" }

Budget

{ "budget": { "dailyTokenLimit": 500000 } }

500,000 tokens/day is the default. It guards in two stages:

  • At 80% Siltpoke soft-caps — it degrades to on_demand, so Brain only runs when you /siltpoke-wake it.
  • At the hard stop the pet flips to sleeping_broke and skips Brain until the midnight reset.

dailyTokenLimit: 0 disables the gate entirely. Cache-read tokens count at only 0.1× toward the limit, so warm-prefix calls barely dent it.

Quiet hours

Off by default — no window is set, so Siltpoke can trigger at any hour. To mute it overnight, set your own window:

{ "quietHours": { "start": "23:00", "end": "08:00" } }

Brain is skipped for any trigger inside the window; everything outside it runs normally.

Cost

Each critic call runs about $0.03 (median $0.031 from real logs; most land in $0.01–$0.09). Prompt caching keeps a ~65k-token prefix warm, so most of every call is cached-read at a fraction of the price — only the ~1.5k tokens it writes are billed in full. Daily cost scales with how many turns you review: a few reviews is a few cents, a heavy day runs around a dollar, and the budget cap above bounds it either way.

Personality keys

The five personality dials also live in config.json, each 010:

{ "snark": 7, "patience": 4, "rigor": 6, "chattiness": 5, "curiosity": 8 }

Re-roll them anytime without touching the rest of the config:

bun run edit-personality

See the Personality section for what each dial changes and the 16 archetypes they combine into.

Personality

Every pet is a species (an ASCII face + starting dial values) plus five dials you can tune from there. The dials combine into one of 16 archetypes, and XP moves you through 9 titles. You already saw the ways to set this up during install (defaults, by hand, random, the 6-question quiz, or reading your ~/.claude/ memory — plus the mirror/complement/hybrid match mode) — this section is the reference for what each piece means.

Species

Pick a face, then fine-tune any dial afterward — the species only sets the starting values.

slime

 .---.
 (o.o)
 (___)

easygoing. lets small stuff slide, speaks up when it counts. Preset dials — snark 5 · patience 5 · rigor 5 · chattiness 5 · curiosity 5

cat

 /\_/\
 (o.o)
 > ^ <

opinionated. trigger-happy on smells, judges your naming. Preset dials — snark 8 · patience 2 · rigor 4 · chattiness 5 · curiosity 7

owl

 ,-,-,
 (O,O)
 =====

meticulous. high rigor, rarely wrong, never loud. Preset dials — snark 3 · patience 8 · rigor 9 · chattiness 5 · curiosity 8

robot

 [---]
 |o-o|
 [___]

literal. deterministic critiques, cites every line. Preset dials — snark 2 · patience 7 · rigor 10 · chattiness 2 · curiosity 3

bunny

 (\_/)
 (o.o)
 (=v=)

chatty. warm and talkative, always rooting for you, big heart. Preset dials — snark 1 · patience 9 · rigor 5 · chattiness 8 · curiosity 5

Dials

Five dials, each 010. A new pet starts from its species preset above — not a flat 5 — and you tune from there:

Dial Low (0) High (10) What it changes
snark sweet savage how sharp the tone is — sweet jokes to savage roasts
patience trigger chill how fast it flags — chill to trigger-happy
rigor vibes methodical how it decides — gut feel to citing every line
chattiness terse chatty how much it says — terse to talkative
curiosity incurious digs in how deep it digs — and the Curious / Steady prefix

curiosity also prefixes your archetype name: ≥7 adds Curious, ≤3 adds Steady.

Dials aren't frozen. As Siltpoke learns your style over a project, each can drift up to ±3 from where you set it — a pet you made terse can loosen up if you keep engaging with its longer notes, and tighten back if you don't.

Archetypes

snark × patience × rigor × chattiness form a 4-bit key, in that order — each dial counts as 1 (high) at ≥6 or 0 (low) below that. The 16 combinations each have a name:

Key Name What it's like ~MBTI
1000 The Snapper quick, sharp comments, no follow-up — then walks away ISTP
1001 The Rant long impassioned tirades, high energy, low evidence ESTP
1010 The Hawk sharp-eyed, low-tolerance, surgical — hunts in silence ISTJ
1011 The Drill Sergeant yells out every typo; cares deeply, expressed as volume ESTJ
1100 The Tease playful jabs; doesn't actually want you to feel bad INTP
1101 The Roaster sharp tongue, big heart, loves an audience ENTP
1110 The Sly Fox sees everything, says little, lands devastating one-liners INTJ
1111 The Wry Coach snarky mentor — will roast you and teach you ENTJ
0000 The Worrier quietly anxious, vibes-based, doesn't share much ISFP
0001 The Frantic anxious and chatty, all volume — a loveable mess ESFP
0010 The Perfectionist anxiously careful; worries visibly, never yells ISFJ
0011 The Anxious Officer frets out loud about every detail — means well ESFJ
0100 The Zen Monk lets everything slide; vibes only, dreamy detachment INFP
0101 The Cheerleader sunshine — patient, chatty, no rigor, hypes you up ENFP
0110 The Patient Sage quiet, methodical, kind; cites evidence gently INFJ
0111 The Saint patient, careful, warm, vocal — pure good ENFJ

The ~MBTI column is a loose, just-for-fun shorthand — Siltpoke scores five Big-Five-style dials, not MBTI's four axes, so read it as vibes-adjacent, never a real type. The rough fit: snark≈T/F, rigor≈J/P, chattiness≈E/I, and curiosity≈S/N through the Curious/Steady prefix — a Curious Hawk leans INTJ, a Steady Hawk stays ISTJ. The ~MBTI column above is the full 16-way mapping.

XP, levels & titles

XP sources:

  • The critic reviewing your work — when it writes a critique it can award XP directly, sized by the model to what it saw (a fix, a test, a clean refactor). This is the main way you level, and it's separate from the 100-per-day cap on interaction XP below.
  • Forwarding a fresh critique: +10 XP.
  • Petting: +5 XP — no per-pet limit; bounded only by the shared 100 XP/day cap.
  • All dashboard/action XP shares a combined 100 XP/day cap.
  • XP only ever climbs — a wrong critique is dismissed, never docks you.

Level curve (tiered, not flat):

  • Levels 1–5: 100 × n
  • Levels 6–10: 250 × n
  • Levels 11–20: 500 × n
  • Levels 21+: 1000 × n

(n is your current level — the XP to leave level n for n + 1. Level 1 → 2 costs 100 × 1; level 2 → 3 costs 100 × 2.)

The curve stays gentle early so unlocks show up fast, then steepens — reaching Sentinel (L10) and beyond is months of engagement, not days.

The 9 titles:

Level Title Meaning
1 Hatchling fresh out of the egg
2 Watcher starts watching your sessions
5 Apprentice learning your style
10 Sentinel a trusted guard
13 Veteran seasoned reviewer
20 Sage a wise resident
30 Oracle deep foresight
50 Ancient a long-lived companion
100 Legend the pinnacle

Memory

Siltpoke's cross-session memory splits into two stores. One travels with the pet everywhere; the other stays put in each repo.

Two stores, one brain

  • GLOBAL~/.siltpoke/global.json — the pet's identity: name, species, appearance, base personality dials, and the XP ledger (level, streak, bond meter). One identity across every project.
  • PER-PROJECT~/.siltpoke/projects/<project_id>/memory.json — what it learned in this repo: facts, learned rules, recent chat sessions, an episodic log of what happened (event fragments), a long-term summary, personality drift over the base, and your goals & constraints. Scoped to one project — <project_id> is the project's stable id (/siltpoke-where prints it for the current directory).

Both stores feed a single Brain prompt, assembled in cache-friendly order, so a bug from last week can inform today's critique without re-sending the whole history.

Not everything reaches every place, though — and this is the part that actually shapes behavior. The critic only ever sees your style facts: conventions and rules about the code (tabs over spaces, never force-unwrap, your naming preferences). Personal facts — your pet's name, what you're building, who you are — are deliberately held back from critiques; they'd only add noise to a code review. The pet chat sees all of it, so it can talk to you like it knows you while the critic stays focused on the code.

Facts you control

Facts move through a lifecycle: pending → active → retired.

  • /siltpoke-remember [--type fact|goal|constraint] "claim" — stores a claim you tell it directly. Because you asserted it yourself, it lands active and pinned immediately (the trust path) — no approval step.
  • /siltpoke-approve <fact-id> — flips a pending fact to active, so it becomes part of Siltpoke's working memory. Pending is where facts proposed during consolidation wait: when Siltpoke compacts a session, the new durable facts it distills land pending for your say-so. Two kinds skip the queue straight to active: facts you assert with /siltpoke-remember, and facts it auto-captures from what you say in chat.
  • /siltpoke-reject <fact-id> — flips a pending fact to retired.
  • Pinned facts are exempt from all of the above automation — once pinned, a fact is protected from decay and stays active until you change it yourself.

How does it decide what's worth keeping? It's a hybrid. Plain code decides whether a message even looks like a durable fact worth spending on — and only then does a small model (Haiku) distill it, with a "would this still matter next week?" test. The choice to spend a model call is always made in code, never left to the LLM.

The /memory view lists what it's holding for the current project — for example:

$ /memory

active (3)
  ● deploys via GitHub Actions          workflow    confirmed
  ● prefers tabs over spaces            style       confirmed
  ● never force-unwrap in Swift         pref        pinned

pending (1)
  ○ uses pnpm, not npm                  tooling     /siltpoke-approve to keep

Consolidation

Roughly every ~10 stops or 7 days — whichever comes first — Siltpoke compacts recent facts, critiques, and dismissals into the long-term summary, so the working memory doesn't grow without bound. Two fields gate this: last_consolidated_at (when it last ran) and consolidation_due_at (when it's next allowed to). If there's nothing new since the last pass, it skips the run.

Decay & prune

Stale, low-confidence facts are retired — but never silently. Siltpoke scores each active fact by recency, confidence, and how often it's been recalled; once a fact falls stale enough, Siltpoke proposes it for retirement (retire_proposed) and you confirm before it's actually marked retired with retired_reason: "decayed". Facts are never hard-deleted — only their status changes, so nothing you told Siltpoke is truly gone. Pinned facts are exempt from decay entirely.

Reflexion — the critic self-corrects

/siltpoke-dismiss <id> "why it was wrong" does more than clear a critique. Siltpoke reads your reason and, when it's confident, writes a learned rule back into memory — with provenance, so you can always trace where a rule came from:

from dismissing critique <id>: <reason>

That rule feeds every future Brain call, so the critic won't repeat the same wrong call twice. A dismiss without a reason clears the critique but teaches Siltpoke nothing. /siltpoke-undismiss <id> reverses a dismissal — it flips the critique back to pending and removes the rule it created, if that dismissal is what created it.

Repo Graph

The fastest way to end up with a codebase you don't understand is to vibe-code one. Repo Graph turns what your agent built into a structural map you can actually read back.

Build the graph

/siltpoke-index [--force] walks your repo (.ts/.tsx/.js/.jsx/.py, skipping node_modules, dist, .git, and the usual build/cache dirs) and parses it into a structural graph — deterministic, no LLM involved. It's incremental: re-running only re-parses files whose content changed since the last index; pass --force to rebuild from scratch. The graph is stored under ~/.siltpoke/repo-memory/<proj-hash>/ and is the substrate every other repo-graph feature below reads from — refresh it any time your source has moved on.

See the shape

/siltpoke-graph opens a C4 container view: a system boundary (your repo, by name) containing containers grouped into layer bands. It has two modes, toggled at the top:

  • Auto (the default) — a deterministic projection of the structural graph. No LLM, instant, free. Containers are your source subdirectories; edges are neutral imports ×N (how many imports cross between two groups); bands come from your own CLAUDE.md Architecture section (or, if you haven't written one, a fallback grouping by top-level directory). Siltpoke's own repo, for example, bands into surfaces · core · state · infra. Every node and edge here is a plain structural fact.
  • Generated (opt-in, paid) — one whole-repo pass through the Brain that proposes a richer model: verb-labeled edges (calls, reads, writes…), inferred layer ordering, and short descriptions. Every claim is then grounded against the real graph — a grounded % chip shows the share that cite real code, and a band whose proposed order contradicts the actual import gradient is demoted to a dashed inferred layer rather than asserted. It spends tokens (a soft ~$0.6 / hard $2 cap), so it's a button you press, not the default.

The whole architecture fits on one screen; click a container to drill arch → file → symbol, descending from the container view into its files and then into the symbols each file defines.

Trace a path

Path Highlight follows the spine from an entrypoint — a detected root, or any function you pick — showing how a request actually flows, call by call, built deterministically from the same graph and never invented. Each call is classified by how confidently it resolves: a callee that resolves to exactly one definition is resolved; one with more than one candidate is inferred; one that can't be resolved at all (dynamic dispatch, or simply not found) is unresolved and left off the path rather than guessed at. That mix rolls up into a coverage score — the share of in-repo call sites that resolve (external, standard-library, and dynamic calls are left out of the count) — shown as a tier (green ≥60% / yellow ≥40% / red) so you know how much of the path to trust outright versus double-check yourself.

Ask it, grounded

/siltpoke-explain <target> [--depth 1|2] explains a file, function, or symbol in plain language, grounded in the graph /siltpoke-index built. An evidence guard parses every [file:line] citation the answer makes and checks it against the real graph — the file must exist and the line must fall inside it. The fraction of citations that pass is the grounded %; drop below 90% and the answer carries a visible low confidence banner rather than passing silently as fact. --depth 2 pulls in transitive callers for a wider view; --force skips the cache and re-asks Brain.

Chat anchored to a node

Opening a chat from a node in the graph pins that conversation to it — once, at creation. The surrounding subgraph is resolved and frozen alongside the conversation as its working context, so the answer stays reproducible against the version of the code you were looking at when you pinned it. Both the conversation and its anchor persist to disk, so the chat remembers across sessions, not just within one. If the file behind the anchor changes before you send another message, Siltpoke flags it as stale and lets you choose: keep answering against the pinned version, or re-anchor to the current one.

Commands

The commands used above — /siltpoke-index, /siltpoke-graph, /siltpoke-explain, /siltpoke-where, /siltpoke-relocate — are listed with their full flags under Slash commands → Repo Graph commands.

Architecture

Your agent reviewing its own work is a blind spot. Siltpoke is a second, independent one — a separate claude -p subprocess with a different prompt, different personality, and different blind spots, running on the login you already have. No extra LLM bill.

The review loop

   your agent finishes a turn
             │
             ▼
   ┌───────────────────┐
   │  1. Stop hook     │ ── transcript + diffs + tool output
   └─────────┬─────────┘
             ▼
   ┌───────────────────┐
   │  2. Gate chain    │ ── quiet hours? budget? no change? → skip
   └─────────┬─────────┘    (never blocks your agent)
             ▼
   ┌───────────────────┐
   │  3. Brain (LLM)   │ ── separate `claude -p` + tsc/eslint/ripgrep
   └─────────┬─────────┘
             ▼
   ┌───────────────────┐
   │  4. Classify +    │ ── suppress · bubble · full critique
   │     evidence-guard│    (kept findings cite verbatim tool output)
   └───────────────────┘
  1. Stop hook fires — your agent finishes a turn. The hook hands off your transcript, diffs, and tool output.
  2. Gate chain — skips on quiet hours, the budget cap, or no change since the last run. Never blocks your agent — this is pull, not push, all the way down.
  3. Brain reasons — a separate claude -p subprocess reviews the work with tsc / eslint / ripgrep evidence. It runs on your existing Claude Code login, so what it costs is tokens, not a separate bill.
  4. Classify, then guard — the tool output is sorted into three outcomes: nothing usable → suppressed; clean but with changes → a quiet one-line bubble; a real signal → a full critique. Before any critique is kept, an evidence guard checks that every cited snippet is a verbatim substring of the real tool output — anything it can't ground is dropped, never shown. Critiques are written to disk, never auto-injected into your chat — you pull them in yourself (see Daily use).

Pull, not push. Every critique lands on disk. Nothing enters your chat until you ask for it — so the review can never derail your agent mid-task.

What it reviews

Two layers do the work. First a deterministic rubric — 13 rules run over the diff, most via a tree-sitter AST, no LLM involved:

  • Size & shape — god-file (>500 lines), god-function (>50 lines or high complexity), deep nesting (>3), long parameter lists (>4), and behavior-selecting boolean flag params.
  • Smells — magic numbers, commented-out code, comments that just narrate the next line, defensive over-reach (a try/catch that silently swallows), and sprawling abstractions (an interface with one impl and one caller).
  • Coverage & consistency — a test-gap check (source changed >30 lines with no matching test) plus repo-aware rules that flag code diverging from your own repo's conventions.

Alongside the rubric it runs four CLI tools — tsc, eslint, ripgrep, and git-diff — and hands all of it to the second layer: the Brain (claude -p), which writes the actual prose critique. The rubric hits and tool output are its evidence; the Brain decides what's worth saying and how, in your pet's voice.

Before the Brain writes anything, a quick intent classifier tags the change — bugfix, refactor, feature, chore, or exploration — mostly by regex over your message, commit, and the files you touched, with the Brain filling in the rest. That label sets the critique's framing, so an exploratory spike isn't held to the same bar as a bugfix.

Your personality dials shape that voice — the tone, and how readily it speaks up — but they never touch the hard gates or the rubric thresholds. A patient pet sounds calmer; it doesn't quietly raise the bar on what counts as a real problem.

Inspecting a critique

A critique is never a black box. Every inbox entry expands into an audit card — which rubric rules fired, the Brain's reasoning, and the raw tool evidence behind each claim — and gets a shareable permalink at /critique/:id for anything you want to revisit or pass on.

Every action you take on one — forward, dismiss, ack, or a /siltpoke-feedback note — is appended to a local preference log: the running signal trail behind Siltpoke's learning, kept entirely on your machine.

And for the Brain's own runs, siltpoked emits a trace per Brain turn to a local store, browsable as a waterfall at /traces. The daemon prunes it on a schedule — 30-day retention, 500 MB cap — so it stays a debugging aid, not a disk leak.

Cost

Brain rides your existing Claude Code login, so there's no separate LLM bill — just tokens, kept cheap by prompt caching and bounded by a hard daily cap (budget.dailyTokenLimit) that can never run away. For the actual per-call and per-day figures, see Configuration → Cost.

The daemon

siltpoked is a long-lived Hono service listening on 127.0.0.1:9876. It's the Stop hook receiver and the process the critic, dashboard, and chat all run in — one background service instead of a fresh process per event. Manage it with /siltpoke-daemon (start, start --detach, stop, status, install-autostart). It also auto-spawns lazily from the Stop hook if it isn't already running, so install-autostart is only needed if you want zero cold-start before the first Stop event of a session.

Troubleshooting

/siltpoke-doctor

The install-health diagnostic. It prints a 9-row ✓/✗ checklist and exits 0 if every check passes or 1 if any fails. Each failing row already includes the path and the actionable fix, so read it back verbatim rather than guessing. --json gives machine-readable output for piping into another tool; --quiet prints a one-line summary instead of the full table.

$ /siltpoke-doctor

siltpoke-doctor — checking install health

  ✓  ~/.claude/settings.json valid
  ✓  Stop hook registered (http + command pair)
  ✓  ~/.siltpoke/inner.txt readable
  ✓  ~/.siltpoke/wake.json healthy (absent OK)
  ✓  ~/.siltpoke/global.json schema v3 current
  ✓  slash command symlinks intact (35/35)
  ✓  ~/.siltpoke/config.json valid
  ✓  last Brain call: ok @ 2026-07-01T14:32
  ✓  daemon running latest code

All 9 checks passed. Install healthy.

A failing row looks like this, with the fix inline:

  ✗  slash command symlinks intact (34/35)
       siltpoke-tag-entities.md (missing). Run `bun src/cli/install.ts` to re-link.

Common fixes

How to clear each failing doctor row — most echo the doctor's own message; a couple (schema, config) add a suggested next step:

Problem Fix
settings.json missing re-run the installer (bun src/cli/install.ts)
Stop hook not paired re-run the installer — it's idempotent, safe to run again
global.json schema mismatch possibly a stalled migration — check docs/handoff/ for the most recent schema-related phase
Symlinks broken the repo moved since install — re-run the installer from the new location
config.json missing fields re-run bun src/cli/first-run.ts (the personality wizard)

Common symptoms

  • No face in the statusline — check that ~/.claude/settings.json's statusLine points at the Siltpoke wrapper, then restart Claude Code.
  • Brain never runs — run /siltpoke-stats to see today's spend and trigger mode, then tail -f ~/.siltpoke/brain-calls.jsonl and look for skipped reasons: quiet_hours, budget_hard_stop, no_change, wrong_event_mode, on_demand_no_bypass.

Want a review right now?

/siltpoke-wake is a one-shot bypass of the budget and quiet-hours gates — use it when you want Brain to run on demand instead of waiting for the next gated event.

概览

Siltpoke 是一个独立的「第二大脑」,与你的 CLI 编程 agent 并肩运行。它不替你写代码——而是观察你 agent 的工作、独立地进行推理,并跨会话记忆,这样你就不必只依赖单一模型对自己工作的判断。

它做什么

  • 审查 — 一个独立的 claude -p 子进程会重新审查每一轮对话(不同的提示词、不同的性格,用你已有的 Claude Code 登录),跑一套确定性的 13 条 rubric 外加 tsc/eslint/ripgrep,写出有真实工具证据支撑的评审,而不是凭感觉猜测。
  • 绘图 — 构建你代码库的结构化 repo-graph(文件、符号、调用链),让你能够探索并读懂 agent 实际构建出的东西,而不只是它自称完成的东西。
  • 记忆 — 按项目绑定的跨会话记忆:从你的反馈中学到的规则、过去的 bug 以及它们是如何被解决的、你告诉过它的事实。
  • 成长 — 一只有名字、有种族、有自己性格的 ASCII 宠物。用得越多等级越高;错判只会被驳回,绝不会被惩罚。

一套系统,而非各自独立的工具

评审官、repo-graph、记忆和宠物并不是拼凑在一起的独立功能——它们是一个闭环。评审官学到的东西会汇入记忆;repo-graph 绘制的东西为评审官和对话提供可引用的依据。这些部分中没有哪一个是设计来单独依赖的;它们合在一起,才是一个日常真正好用的第二大脑。

更大生态的一半

Siltpoke 是同一作者打造的一个 Claude Code 生态的一半。两个组件都装进你的 Claude Code、作用在同一批会话上——不是两个要来回切换的独立 app,而是同一个环境里的 两个伴侣:

  • siltpoke — 常驻的陪伴 companion:盯着、记着、温和地提醒。跟着项目走的持久第二大脑。
  • project-life-cycle — 一个 Claude Code skill,给你的工作一套可复用的节奏:可追溯的 spec → plan → execute → ship → release 纪律,由 /init-harness → /ship → /release 驱动。它是一个流程层、不是代码生成器——把工作流固化下来,让你不必每开一个新项目就重讲一遍你的开发流程。

如果说 siltpoke 是硬件——那个始终都在的伴侣——那 project-life-cycle 就是软件:决定你 何时停下来预测、构建、理解的节奏。两者指向同一件事——让你在造东西的同时从中学习,而 不是把一切都甩给 agent。

安装 project-life-cycle(独立 skill——siltpoke 不依赖它,但它们在一起更好用):

claude plugin marketplace add Victoriakaey/project-life-cycle
claude plugin install project-lifecycle@project-life-cycle

重启 Claude Code;当你开新项目或规划里程碑时它会自动加载。之后更新用 claude plugin marketplace update project-life-cycle && claude plugin update project-lifecycle

长远看,两者会逐步深化成一个接缝——siltpoke 读 diff、追问「为什么」的器官,接入 project-life-cycle 的 ship 节奏,让「理解」成为闭环的一部分。这层更紧的整合还在成形; 而今天,它们光是同住在一个 Claude Code 里,就已经在互补了。

适合谁

任何用 CLI 编程 agent 构建的人——目前支持 Claude Code,Codex 支持即将到来。

接下来去哪

想看完整的产品介绍和宠物的实机演示,请看落地页。想开始使用,请继续往下看「安装与设置」。

安装与设置

前置条件

  • 已登录的 Claude Code。
  • Bun ≥ 1.0 — curl -fsSL https://bun.sh/install | bash

步骤

  1. 克隆仓库并安装依赖。

    git clone https://github.com/Victoriakaey/siltpoke ~/siltpoke
    cd ~/siltpoke && bun install
    
  2. 运行安装向导。

    bun run setup
    

    向导会先带你把 Siltpoke 接入状态栏,然后给你的宠物起名字、选种族、定语言,并设定它的性格。

  3. 重启 Claude Code。 退出并重新启动,你应该会在状态栏左侧看到一张 ASCII 脸出现。

  4. 打个招呼。 随便发条消息。约 3 秒内,脸旁边会出现一行气泡——Siltpoke 活了。

Siltpoke 会把自己的脸拼接到你原有状态栏的左侧,并把最新的一条评审放进 一个引号气泡里、贴在宠物名字那一行。所以整行大致长这样:

    /\_/\
    (o.o)
    > ^ <
    Mochi      "这个 if 分支永远不会命中 — parser.ts:88"
  Sentinel
L10 1560/2500

脸块是三行脸,接着是宠物的名字、当前最高的称号、以及等级 + 距下一级的 XP——最新一条评审用引号气泡贴在名字旁边。Siltpoke 把这块拼到你本来在跑的状态栏 (比如 claude-hud)左侧,原状态栏原封不动。

气泡的颜色跟着评审的严重度走:=高、=中、=低或信息性提示—— 而后者占大多数。灰色不是 bug:只有当评审官拿得出 file:line 证据、确认是真问题时, 才会升到灰色以上。没有活跃评审时,气泡停在你配置的颜色(默认青色)。

幂等,且可逆

安装器可以安全地重复运行,并会在改动任何东西之前备份 ~/.claude/settings.json。如果要移除 Siltpoke 并恢复此前的设置:

bun run uninstall

首次运行向导

bun run setup 会依次询问宠物的名字种族语言,然后设定它的五个性格旋钮——毒舌、耐心、严谨、话痨、好奇心。设定旋钮有五种方式:

  • 用默认 — 直接采用上面的种族预设。
  • 手动设定 — 逐个输入五个旋钮的数值,完全掌控。
  • 随机 — 五个旋钮全部随机,来只惊喜宠物。
  • 6 题测验 — 一套简短的 Likert 测试在本地为旋钮打分(数值绝不交给 LLM 猜),并起一个原型名。
  • 读取你的记忆 — 在你明确同意的前提下,Siltpoke 读取你的 ~/.claude/ 文件(CLAUDE.md、规则、记忆),推导出一个契合你风格的性格。(只有找到这些文件时才会提供。)

测验和读取记忆这两种方式还会问它该如何与你相处——即匹配模式,默认是混合

  • 镜像 — 贴合你自己的风格:惜字的程序员配一只惜字的宠物。
  • 互补 — 反过来补你的盲区:惜字的程序员配一只更话痨的宠物。
  • 混合(默认)— 气质旋钮(毒舌、话痨)镜像你、让它感觉熟悉,任务旋钮(严谨、耐心、好奇)互补你、让它抓住你会跳过的东西。

随时可以重新运行向导,无需重新安装——bun run first-run,或用 bun run edit-personality 只重掷性格:

bun run first-run

完整的旋钮与原型说明,见「性格」章节。

日常使用

拉取,而非推送

每一轮对话结束后,一个独立的 claude -p 子进程会在后台审查你 agent 的工作,并把评审写入磁盘。评审绝不会自动注入到你的对话中——它们停留在收件箱里,直到你主动把它们拉进来。什么都不会打断你的节奏,什么时候看,由你决定。

这套循环

  1. 你的 agent 结束一轮 → Siltpoke 在后台运行 → 一条评审进入收件箱。
  2. 想看看有什么在等着时,随时运行 /siltpoke-inbox
  3. 对每一条待处理的评审,你要做的判断:
    • 有用/siltpoke-forward <id> 把它拉进对话(如果是新鲜评审,+10 XP)。
    • 判断错了/siltpoke-dismiss <id> <理由> 驳回它,并触发 Reflexion——Siltpoke 会根据你给的理由写出一条学到的规则,以后不会再犯同样的错。
    • 中立 / 已经看过/siltpoke-ack <id> 只翻转状态,不给 XP,也不触发学习。

/siltpoke-inbox 列出待处理的评审——严重度、时间,和一行气泡摘要:

$ /siltpoke-inbox

ID       SEVERITY  TIMESTAMP            BUBBLE
c-4a1f   high      2026-07-01T14:32:10  tsc: 'session' 可能为 undefined — auth.ts:42
c-2e07   medium    2026-07-01T14:05:44  这个 if 分支永远不会命中 — parser.ts:88
c-0758   info      2026-07-01T13:56:12  4 个文件、13 处改动,检查全绿

转发其中一条,会把完整评审拉进你的对话——问题、位置,以及一个具体的修法:

$ /siltpoke-forward c-4a1f

  [HIGH · 类型安全]  auth.ts:42
  这里的 `session` 可能是 undefined —— 上面的 guard 只检查了 `token`,
  所以在未登录路径上 `session.userId` 会抛错。
  → 先加个 guard:  if (!session) return null
  evidence: tsc —— "'session' is possibly 'undefined'" at auth.ts:42

你真正会用到的 3 个命令

命令 作用
/siltpoke-inbox 列出待处理的评审(只读)
/siltpoke-forward <id> 把某条评审拉进对话(新鲜评审 +10 XP)
/siltpoke-dismiss <id> <理由> 驳回一条评审,让 Siltpoke 从错误中学习

/siltpoke-ack <id> 补齐了中立情形——不给 XP,不触发 Reflexion,只是标记为已读。

XP 基础规则

Siltpoke 随你使用而升级——主要来自它审查你工作时写的评审,外加转发一条评审 (+10)或抚摸它(+5)这些小额。XP 只会往上涨;判断错误的评审会被驳回并 喂给 Reflexion,永远不会扣你的分。完整拆解见**「性格 → XP、等级与称号」**。

命令参考

安装好 Siltpoke 后,下面每一条都是 Claude Code 里可用的 /siltpoke-* 斜杠命令。带 XP 的命令都遵守共享的每天 100 XP 动作上限。

不带后缀的 /siltpoke 会打印一张状态卡——你的宠物一目了然:

┌─ Siltpoke ─────────────────────────────────────
│ Mochi the cat
│ level 10  [████████████░░░░░░░░] 62%  (1560/2500 XP)
│ titles: Hatchling, Watcher, Apprentice, Sentinel
│ mood: idle
│ today: 12 brain calls, 0 reflections, $0.0800 spent
│ mode: gates
│ last 7d: ▂▄▆▃▅▆█
│
│ projects:
│   myapp     12 calls today  ·  3 pending
└────────────────────────────────────────────────

评审相关

命令 说明
/siltpoke-inbox 列出待转发的评审
/siltpoke-forward <id> 把某条评审拉进对话(新鲜评审 +10 XP)
/siltpoke-forward-all 一次性拉出所有待处理的评审
/siltpoke-last 把 Siltpoke 最近一条评审拉进对话
/siltpoke-dismiss <id> [理由] 驳回一条评审,让 Siltpoke 从错误中学习
/siltpoke-undismiss <id> 撤销此前的驳回——把状态改回待处理,并撤销对应的学习规则
/siltpoke-ack <id> 确认一条评审。中立状态翻转——不给 XP,不触发 Reflexion
/siltpoke-feedback <id> <文本> 对某条评审提交自由文本反馈。写入偏好日志,未来会用于 Reflexion 规则生成
/siltpoke-review 让 Siltpoke 在下一条消息时审查你的工作。为这一次 Brain 调用绕过预算与安静时段限制

宠物与照料

命令 说明
/siltpoke 显示 Siltpoke 的状态卡——名字、等级、心情、今日花费、触发模式
/siltpoke-pet 抚摸 Siltpoke,获得 +5 XP(共享每天 100 XP 上限)
/siltpoke-feed 喂食 Siltpoke。+3 饱食度,+1 心情,+5 XP(共享每天 100 XP 上限)
/siltpoke-clean 给 Siltpoke 清洁。+1 血量,+1 心情,+5 XP(共享每天 100 XP 上限)
/siltpoke-play 和 Siltpoke 玩耍。+2 心情,+1 亲密度,−1 饱食度,−2 精力,+3 XP(共享上限)
/siltpoke-tease 逗弄 Siltpoke。−2 心情,−1 亲密度。0 XP。一天 3 次以上会变得暴躁
/siltpoke-sleep 让 Siltpoke 睡觉。+1 血量,+4 精力。0 XP,不设上限

状态与花费

命令 说明
/siltpoke-stats 显示 Siltpoke 今日花费、预算状态与触发模式
/siltpoke-report 在后台启动 Siltpoke 的 dashboard 服务,并在浏览器打开 http://127.0.0.1:9876
/siltpoke-report-stop 停止后台运行的 Siltpoke dashboard 服务

守护进程与门控

命令 说明
/siltpoke-daemon 管理 Siltpoke 守护进程(siltpoked)——启动、停止、查看状态、安装自启动
/siltpoke-wake 为这一次 Brain 调用绕过预算与安静时段的限制
/siltpoke-mute 让 Siltpoke 在一段时间内保持安静。mute 优先级高于 wake——生效期间评审官不会触发
/siltpoke-unmute 清除 Siltpoke 的静音标记,让评审官重新触发

记忆与事实

命令 说明
/siltpoke-remember [--type fact|goal|constraint] "内容" 为 Siltpoke 这只宠物记住一条事实、目标或约束
/siltpoke-approve <fact-id> 批准一条待定事实(pending → active)
/siltpoke-reject <fact-id> 拒绝一条待定事实(pending → retired)
/siltpoke-tag-entities 手动对 memory.json 运行实体打标工作(为未打标的生效事实补充实体信息)

Repo Graph 相关命令

命令 说明
/siltpoke-index [--force] 为当前项目构建 / 刷新 Siltpoke 的结构化 repo-graph(确定性生成,不调用 LLM)
/siltpoke-graph 在浏览器中打开 Siltpoke 的 repo-graph 可视化面板。自动启动守护进程,地址为 http://127.0.0.1:9876/repo-graph
/siltpoke-explain <target> [--depth 1|2] [--force] [--json] 借助 repo-graph + Brain 解释某个文件 / 函数 / 符号
/siltpoke-where 打印当前工作目录对应的、已解析出的 Siltpoke 项目身份
/siltpoke-relocate 更新当前 Siltpoke 项目记录的根路径(project_id 保持不变)

对话与健康检查

命令 说明
/siltpoke-chat 通过守护进程与 Siltpoke 的 AI 对话(单轮、REPL 或搜索模式)
/siltpoke-doctor 安装健康诊断——9 项 ✓/✗ 检查(settings、hooks、inner.txt、wake、schema、符号链接、config、brain-health、daemon 新鲜度)
/siltpoke-help Siltpoke 使用手册——斜杠命令、日常流程、配置、故障排查

配置

所有配置都在 ~/.siltpoke/config.json 里。可以直接手动编辑,也可以用 bun run edit-personality 重新生成性格那部分。配置文件会在下一次 Brain 调用时被重新读取——不需要重启。

触发模式

决定 Siltpoke 什么时候真正运行 Brain(那次 claude -p 审查):

模式 行为
always 每一个受支持的事件都触发(最费 token)
gates(默认) 只在配置好的门控事件上触发(默认是 Stop + PreCompact
on_demand 从不自动触发,除非刚用过 /siltpoke-wake
hybrid gates 的基础上,/siltpoke-wake 始终可以绕过限制

直接设置:

{ "triggerMode": "on_demand" }

预算

{ "budget": { "dailyTokenLimit": 500000 } }

默认是每天 500,000 token。它分两级把关:

  • 80% 时 Siltpoke 软性封顶——降级为 on_demand,Brain 只在你 /siltpoke-wake 时才跑。
  • 硬性上限时宠物翻转为 sleeping_broke,Brain 一直跳过,直到午夜重置。

dailyTokenLimit: 0 会彻底关闭这道闸门。缓存读取的 token 只按 0.1× 计入上限, 所以命中热前缀的调用几乎不占额度。

安静时段

默认关闭——不设置任何时间窗口,Siltpoke 在任何时间都可以触发。如果想让 它在夜里保持安静,自己设置一个时间窗口:

{ "quietHours": { "start": "23:00", "end": "08:00" } }

窗口内的任何触发都会被跳过 Brain;窗口外照常运行。

花费

每次评审调用约 $0.03(真实日志中位数 $0.031;多数落在 $0.01–$0.09)。 提示词缓存会把一段 ~65k token 的前缀保持在热缓存里,所以每次调用的大部分 都是缓存读取、只按零头计费——只有它写出的约 1.5k token 是全价。每天花费随你 审多少轮而变:审几轮就是几美分,重度的一天在一美元上下,而上面的预算上限无论 如何都会兜住。

性格相关的键

五个性格旋钮也存在 config.json 里,每个都是 010 之间的数字:

{ "snark": 7, "patience": 4, "rigor": 6, "chattiness": 5, "curiosity": 8 }

随时可以只重新生成这部分,不动配置的其他内容:

bun run edit-personality

每个旋钮具体改变什么、它们如何组合出 16 种原型,见「性格」章节。

性格

每只宠物由一个种族(一张 ASCII 脸 + 一组起始旋钮数值)加上五个可调旋钮 组成。旋钮的组合决定 16 种原型之一,而 XP 会带你走过 9 个称号。安装时你 已经见过设定性格的多种方式(用默认、手动、随机、6 题测验、读取你的 ~/.claude/ 记忆——外加镜像/互补/混合匹配模式)——这一节是每个部分具体 含义的参考手册。

种族

先挑一张脸,之后任何一个旋钮都可以再调——种族只是给出起始数值。

slime(史莱姆)

 .---.
 (o.o)
 (___)

随和。小事不计较,关键时刻才开口。 起始旋钮 — 毒舌 5 · 耐心 5 · 严谨 5 · 话痨 5 · 好奇 5

cat(猫)

 /\_/\
 (o.o)
 > ^ <

有主见。闻到坏味道就开火,还嫌你命名难看。 起始旋钮 — 毒舌 8 · 耐心 2 · 严谨 4 · 话痨 5 · 好奇 7

owl(猫头鹰)

 ,-,-,
 (O,O)
 =====

细致。极其严谨,几乎不出错,从不大声。 起始旋钮 — 毒舌 3 · 耐心 8 · 严谨 9 · 话痨 5 · 好奇 8

robot(机器人)

 [---]
 |o-o|
 [___]

一板一眼。评审确定性强,逐行引用。 起始旋钮 — 毒舌 2 · 耐心 7 · 严谨 10 · 话痨 2 · 好奇 3

bunny(兔子)

 (\_/)
 (o.o)
 (=v=)

话痨。热情健谈、总在为你加油、热心肠。 起始旋钮 — 毒舌 1 · 耐心 9 · 严谨 5 · 话痨 8 · 好奇 5

旋钮

五个旋钮,各自 010。新宠物从它的种族预设(见上)起步——不是一律 5——你再从那儿微调:

旋钮 低(0) 高(10) 具体改变什么
snark(毒舌) 刻薄 语气有多冲——从温柔玩笑到毒舌开喷
patience(耐心) 急脾气 佛系 出手有多快——从佛系到急脾气
rigor(严谨) 凭感觉 有条理 怎么下判断——从凭感觉到逐行引用
chattiness(话痨) 惜字 健谈 话有多少——从惜字到话痨
curiosity(好奇) 无所谓 刨根问底 刨得有多深——并决定「好奇 / 沉稳」前缀

curiosity(好奇)还会给原型名加前缀:≥7 加「好奇·」,≤3 加 「沉稳·」。

旋钮不是固定的。随着 Siltpoke 在一个项目里学你的风格,每个旋钮都可能从你设的 值漂移 ±3——你调得惜字的宠物,如果你老是理会它更长的批注,会渐渐话多起来; 不理会又会缩回去。

原型

snark(毒舌)× patience(耐心)× rigor(严谨)× chattiness(话痨) 按这个顺序组成一个 4 位二进制键——每个旋钮 ≥6 记为 1(高),低于则记 为 0(低)。16 种组合各自有名字:

名字 大概什么样 ~MBTI
1000 暴脾气 又快又冲,撂下一句就走,没有下文 ISTP
1001 喷子 长篇激情开喷,能量拉满、证据稀薄 ESTP
1010 鹰派 眼尖、零容忍、外科手术般精准——闷声狩猎 ISTJ
1011 教官 逮到每个 typo 就吼;其实很上心,只是用嗓门表达 ESTJ
1100 调侃者 爱开玩笑地戳你,但并不真想让你难受 INTP
1101 吐槽王 嘴利心善,就爱有观众 ENTP
1110 狡狐 什么都看在眼里、话少,一开口就是致命一击 INTJ
1111 毒舌教练 毒舌导师——既会损你、也会教你 ENTJ
0000 操心鬼 默默焦虑、凭感觉,话不多 ISFP
0001 慌张鬼 又焦虑又话痨,全是嗓门——可爱的一团乱 ESFP
0010 完美主义者 焦虑地细心;不吼,只是把担心写在脸上 ISFJ
0011 焦虑警官 每个细节都念叨出声——但出于好意 ESFJ
0100 禅僧 什么都由它去;只讲感觉,梦一般地超脱 INFP
0101 啦啦队长 阳光——耐心、话痨、不讲严谨,专门给你打气 ENFP
0110 耐心贤者 安静、有条理、温和;轻声细语地摆出证据 INFJ
0111 圣人 耐心、细致、温暖、爱出声——纯粹的好 ENFJ

~MBTI 这列只是个宽松、图一乐的速记——Siltpoke 打的是五个 Big-Five 风格的 旋钮、不是 MBTI 的四条轴,所以把它当**「气质相近」**看,别当真正的类型。大致 对应:snark≈T/F、rigor≈J/P、chattiness≈E/I、curiosity≈S/N(靠「好奇/ 沉稳」前缀)——好奇·鹰派偏 INTJ,沉稳·鹰派还是 ISTJ。上面的 ~MBTI 那列就是完整 的 16 项对照。

XP、等级与称号

XP 来源:

  • 评审官审查你的工作 —— 它写一条评审时可以直接给 XP,具体多少由模型按它看到的 东西(一个修复、一个测试、一次干净的重构)来定。这是你升级的主要途径,且独立于 下面那个每天 100 的动作 XP 上限。
  • 转发一条新鲜评审:+10 XP
  • 抚摸(pet):+5 XP——没有单独的次数上限,只受共享的每天 100 XP 上限约束。
  • 所有 dashboard / 动作类 XP 共享一个 每天 100 XP 的总上限。
  • XP 只会往上涨——判断错误的评审会被驳回,永远不会扣你的分。

等级曲线(分段,不是固定倍数):

  • 1–5 级:100 × 等级
  • 6–10 级:250 × 等级
  • 11–20 级:500 × 等级
  • 21 级以上:1000 × 等级

等级 指你当前所在的等级——即从当前等级升到下一级所需的 XP。1 级升 2 级花 100 × 1,2 级升 3 级花 100 × 2。)

曲线前期放得很松,让解锁很快出现;之后逐渐变陡——升到哨兵(L10)及以上 需要数月的持续使用,而不是几天。

9 个称号:

等级 称号 含义
1 雏鸟 刚破壳而出
2 守望者 开始盯着你的会话
5 学徒 在学你的风格
10 哨兵 值得信任的守卫
13 老兵 身经百战的评审
20 贤者 睿智的常驻者
30 先知 深谋远虑
50 远古 长寿的伙伴
100 传奇 巅峰

记忆

Siltpoke 的跨会话记忆分成两个存储:一个随宠物走到哪带到哪,另一个留在 每个项目里不动。

两个存储,一个大脑

  • 全局(GLOBAL)~/.siltpoke/global.json — 宠物的身份:名字、 种族、外观、基础性格旋钮,以及 XP 账本(等级、连续天数、羁绊值)。 一个身份,贯穿所有项目。
  • 单项目(PER-PROJECT)~/.siltpoke/projects/<project_id>/memory.json — 它在这个项目里学到的东西:事实、学到的规则、近期对话会话、一份 情节日志(发生过什么,event fragments)、长期摘要、相对基础性格的 漂移,以及你的目标与约束。只对一个项目生效——<project_id> 是该项目的 稳定 id(在当前目录下运行 /siltpoke-where 可以打印出来)。

两个存储会一起喂进同一个 Brain 提示词,并按缓存友好的顺序组装,因此 上周的一个 bug 可以为今天的评审提供参考,而不用每次都重新发送完整 历史。

不过不是所有东西都会进到每一处——而这一点正是真正影响行为的地方。 评审官只看得到你的风格事实:关于代码的惯例和规则(用 tab 不用空格、 Swift 里绝不 force-unwrap、你的命名偏好)。个人事实——宠物的名字、你在做什么、 你是谁——被刻意挡在评审之外;它们只会给一次代码评审添噪音。宠物对话则 看得到全部,所以它能像认识你一样跟你聊,而评审官始终专注在代码上。

由你掌控的事实

事实会经历一个生命周期:pending → active → retired(待定 → 生效 → 退役)。

  • /siltpoke-remember [--type fact|goal|constraint] "内容" —— 记住一条你直接告诉它的主张。因为是你亲口断言的,它会立刻落在 active 并被钉住(信任路径)——不需要审批。
  • /siltpoke-approve <fact-id> —— 把一条 pending 事实翻转为 active, 正式纳入 Siltpoke 的工作记忆。待定(pending)是整合时提出的事实待命的地方: Siltpoke 整合一次会话时,提炼出的新持久事实会落为 pending、等你定夺。 有两类会直接跳到 active:你用 /siltpoke-remember 亲口断言的,以及它从你 对话里自动捕获的。
  • /siltpoke-reject <fact-id> —— 把一条 pending 事实翻转为 retired
  • 被钉住(pinned)的事实不受上述自动流程影响——一旦被钉住, 这条事实就不会被自动衰减,会一直保持生效,直到你自己去改动它。

它怎么决定什么值得记?是个混合机制。先由普通代码判断一条消息是否看起来像 一条值得花力气记的持久事实——只有到这一步,才让一个小模型(Haiku)去提炼它, 并用「这条到下周还重要吗?」来检验。花不花这次模型调用,永远由代码决定,绝不 交给 LLM。

/memory 视图会列出它为当前项目保存的东西——举个例子:

$ /memory

active (3)
  ● deploys via GitHub Actions          workflow    confirmed
  ● prefers tabs over spaces            style       confirmed
  ● never force-unwrap in Swift         pref        pinned

pending (1)
  ○ uses pnpm, not npm                  tooling     /siltpoke-approve to keep

整合(Consolidation)

大约每 ~10 次停止或每 7 天——以先到者为准——Siltpoke 会把近期的 事实、评审与驳回压缩进长期摘要,避免工作记忆无限增长。这个过程由 两个字段把关:last_consolidated_at(上次运行的时间)与 consolidation_due_at(下次允许运行的时间)。如果自上次整合以来 没有新内容,它就会跳过这次运行。

衰减与修剪(Decay & prune)

陈旧、低置信度的事实会被退役——但绝不会悄无声息。Siltpoke 会按新近度、 置信度、以及被唤起的频率给每条生效中的事实打分;一旦某条事实衰减到 足够陈旧,Siltpoke 会提议将它退役(retire_proposed),等你确认 后才真正标记为 retired,并附上 retired_reason: "decayed"。事实 绝不会被硬删除——只会改变状态,所以你告诉过 Siltpoke 的东西 不会真正消失。被钉住的事实完全不参与衰减。

Reflexion——评审官的自我纠正

/siltpoke-dismiss <id> "为什么它错了" 做的不只是清掉一条评审。 Siltpoke 会读取你给出的理由,如果它足够有把握,就会把一条学到的 规则写回记忆——并附上来源,让你随时能追溯这条规则从何而来:

from dismissing critique <id>: <reason>

这条规则会喂进此后每一次 Brain 调用,让评审官不会把同一个错判再犯 一遍。不带理由的驳回只会清掉这条评审,不会教会 Siltpoke 任何东西。 /siltpoke-undismiss <id> 可以撤销一次驳回——把评审翻回待处理状态, 并且如果这次驳回正是创建那条规则的源头,就一并把规则移除。

Repo Graph

最快得到一个你看不懂的代码库的方法,就是凭感觉一路 vibe-code。 Repo Graph 把你的 agent 写出来的东西,变成一张你真正能读懂的结构图。

构建这张图

/siltpoke-index [--force] 会遍历你的仓库(.ts/.tsx/.js/.jsx/ .py,跳过 node_modulesdist.git 以及常见的构建 / 缓存 目录),把它解析成一张结构图——确定性生成,不涉及任何 LLM。它是 增量式的:重新运行时只会重新解析自上次建图以来内容发生变化的文件; 加上 --force 则忽略缓存、从头重建。图会存放在 ~/.siltpoke/repo-memory/<proj-hash>/ 下,是下面每一个 repo-graph 功能读取的基底——只要源码有变动,随时可以刷新它。

一眼看清结构

/siltpoke-graph 会打开一张 C4 容器视图:一个系统边界(就是你的 仓库本身,以仓库名标注),里面的容器按分层带分组。它有两种模式,顶部可切换:

  • Auto(默认)—— 对结构图的确定性投影。无 LLM、即时、免费。容器就是你的 源码子目录;连线是中性的 imports ×N(两组之间跨了多少条 import);分层带 从你自己的 CLAUDE.md Architecture 章节推导(如果你还没写这一节,则退化 为按顶层目录分组)。以 Siltpoke 自己的仓库为例,它分成 界面层 · 核心层 · 状态层 · 基础层。这里每个节点和连线都是朴素的结构事实。
  • Generated(可选,付费)—— 让 Brain 对整个仓库跑一遍,提出一个更丰富的 模型:带动词的连线(调用、读取、写入……)、推断的分层顺序、以及简短描述。 然后每一条声明都会拿真实的图去 grounding——一个 grounded % 芯片显示其中 引用了真实代码的比例;某个分层带若其提议顺序与真实的 import 梯度矛盾,就会被 降级为一条虚线的 inferred layer(推断层),而不是直接断言。它要花 token (软上限约 $0.6 / 硬上限 $2),所以是一个你主动去按的按钮,不是默认。

整个架构一屏尽览;点一个容器即可下钻 架构 → 文件 → 符号,从 容器视图一路下钻到它包含的文件,再深入到每个文件定义的符号。

追踪一条路径

Path Highlight 会从一个入口——一个自动探测到的根,或你自己挑的任意 函数——顺着主干追踪一次请求究竟如何流动,逐次调用,同样基于这张图 确定性地构建,绝不臆造。路径上的每一次调用都会按解析置信度分类:一个 调用如果解析到唯一一个定义,就是已解析(resolved);如果有多个 候选,就是推断(inferred);如果完全无法解析(动态派发,或者压根 找不到),就是未解析(unresolved),会被排除在路径之外而不是被 猜测填入。这些结果汇总成一个覆盖率分数——仓库内调用点里能解析的 比例(外部库、标准库、动态调用不计入)——以绿 ≥60% / 黄 ≥40% / 红 三档呈现,让你知道这条路径有多少可以直接信、有多少值得你自己再核实一遍。

有据可问

/siltpoke-explain <target> [--depth 1|2] 用大白话解释一个文件、 函数或符号,依据是 /siltpoke-index 建好的那张图。一道证据闸门 会解析回答中每一处 [file:line] 引用,并拿去和真实的图核对——文件 必须存在,行号必须落在文件范围内。通过核对的引用占比,就是有据 可循的百分比;一旦低于 90%,回答就会带上一条显眼的「置信度低」 提示,而不是悄悄地当作事实通过。--depth 2 会把间接调用者也纳入 视野;--force 则跳过缓存、重新向 Brain 发问。

锚定到节点的对话

从图上的某个节点开启一段对话,会把这段对话钉在这个节点上——只在 创建那一刻发生一次。围绕这个节点的子图会被解析出来,并和对话一起 冻结成它的工作上下文,因此这个回答会一直对应你当初钉住时看到的那 版代码,可复现。对话本身和它的锚点都会持久化到磁盘,所以这段对话 是跨会话记忆的,而不只是当次会话有效。如果锚点背后的文件在你发下 一条消息之前发生了变化,Siltpoke 会把它标为过期,并让你选择:继续 沿用钉住时的那个版本作答,还是重新锚定到最新版本。

命令

上面用到的命令——/siltpoke-index/siltpoke-graph/siltpoke-explain/siltpoke-where/siltpoke-relocate——连同完整参数都列在 Slash commands → Repo Graph commands 一节。

架构

让你的 agent 审查自己的工作是个盲区。Siltpoke 是另一个独立的大脑——一个独立的 claude -p 子进程,有不同的提示词、不同的性格、不同的盲区,运行在你已有的 登录之上。不产生额外的大模型账单。

这套评审循环

   你的 agent 完成一轮
             │
             ▼
   ┌───────────────────┐
   │  1. Stop 钩子     │ ── 对话记录 + 改动 + 工具输出
   └─────────┬─────────┘
             ▼
   ┌───────────────────┐
   │  2. 闸门链        │ ── 安静时段?预算?无改动?→ 跳过
   └─────────┬─────────┘    (绝不阻塞你的 agent)
             ▼
   ┌───────────────────┐
   │  3. Brain(LLM)  │ ── 独立 `claude -p` + tsc/eslint/ripgrep
   └─────────┬─────────┘
             ▼
   ┌───────────────────┐
   │  4. 分类 + 校验    │ ── 抑制 · 气泡 · 完整评审
   │     (证据 guard)   │    (留下的都逐字引用工具输出)
   └───────────────────┘
  1. Stop 钩子触发 —— 你的 agent 完成一轮。钩子把你的对话记录、改动和工具 输出交接过来。
  2. 闸门链 —— 遇到安静时段、预算上限、或自上次运行以来无改动时会被跳过。 绝不阻塞你的 agent——从头到尾都是拉取,而非推送。
  3. Brain 推理 —— 一个独立的 claude -p 子进程,带着 tsc / eslint / ripgrep 的证据来审查这次改动。它运行在你已有的 Claude Code 登录上, 所以真正要花的只是 token,而不是另一份账单。
  4. 先分类,再校验 —— 工具输出被分成三种结果:没有可用信号 → 抑制; 干净但有改动 → 一行安静气泡;有真实信号 → 一条完整评审。任何评审被留下 之前,一道**证据校验(evidence guard)**会检查每一处引用的片段都是真实 工具输出里的逐字子串——凡是它 ground 不了的都会被丢弃、绝不展示。评审都 写入磁盘,绝不会自动注入到你的对话里——需要你自己把它们拉进来(见 「日常使用」)。

拉取,而非推送。 每一条评审都落在磁盘上。在你主动要之前,什么都不会进 你的对话——所以评审永远不会在任务中途打断你的 agent。

它审查什么

两层协作。第一层是一套确定性 rubric——13 条规则跑在这次改动上,大多经由 tree-sitter AST,不用 LLM:

  • 体量与形态 —— 巨文件(>500 行)、巨函数(>50 行或复杂度过高)、深层嵌套 (>3)、过长参数表(>4)、以及用布尔标志位切换行为的参数。
  • 坏味道 —— 魔法数字、注释掉的代码、只是复述下一行的注释、防御过度 (try/catch 却把错误默默吞掉)、以及蔓延的抽象(一个接口只有一个实现 + 一个 调用方)。
  • 覆盖与一致性 —— 一条 test-gap 检查(源码改了 >30 行却没有对应测试改动), 外加 repo-aware 规则,标出偏离你自己仓库惯例的代码。

除 rubric 外,它还跑四个 CLI 工具——tsceslintripgrepgit-diff——把这一切交给第二层:Brainclaude -p),由它写出真正的 散文评审。rubric 命中 + 工具输出是它的证据;Brain 决定哪些值得说、怎么说,用你 宠物的口吻。

在 Brain 写任何东西之前,一个快速的意图分类器会给这次改动打标签—— bugfix、refactor、feature、chore 或 exploration——主要靠正则扫你的消息、 commit 和改动的文件,其余由 Brain 补。这个标签决定评审的基调,所以一次探索性的 试写不会被拿去跟 bugfix 一个标准要求。

你的性格旋钮塑造的是那个口吻——语气、以及它多愿意开口——但它们绝不触碰硬性 闸门或 rubric 的阈值。一只有耐心的宠物听起来更平和;它不会悄悄抬高「什么才算真正 的问题」这条线。

检视一条评审

一条评审从不是黑盒。收件箱里每一条都能展开成一张审计卡——哪些 rubric 规则 命中、Brain 的推理、以及每处论断背后的原始工具证据——并带一个可分享的固定链接 /critique/:id,方便你回头查看或转给别人。

你对一条评审做的每个动作——forward、dismiss、ack、或一条 /siltpoke-feedback 备注——都会追加进本地的偏好日志(preference log):Siltpoke 学习背后那条 持续的信号轨迹,完全留在你自己的机器上。

至于 Brain 自己的运行,siltpoked 会为每一轮 Brain 发一条 trace 到本地存储, 可在 /traces 以瀑布图查看。守护进程会按计划清理它——保留 30 天、上限 500 MB——让它始终是调试帮手,而不是磁盘泄漏。

花费

Brain 搭在你已有的 Claude Code 登录上,所以没有单独的 LLM 账单——只有 token, 靠提示词缓存压低、再由一个硬性每日上限(budget.dailyTokenLimit)兜住、绝不失控。 每次调用和每天的具体金额,见**「配置 → 花费」**。

守护进程(daemon)

siltpoked 是一个长驻的 Hono 服务,监听 127.0.0.1:9876。它既是 Stop 钩子的接收方,也是评审官、dashboard、聊天 功能共同运行所在的进程——一个常驻后台服务,而不是每个事件都起一个新进程。 用 /siltpoke-daemonstartstart --detachstopstatusinstall-autostart)来管理它。如果它还没在运行,也会从 Stop 钩子那里懒 启动,所以只有想在一次会话的第一个 Stop 事件之前就做到零冷启动时,才需要 用到 install-autostart

故障排查

/siltpoke-doctor

安装健康诊断。会打印一份 9 行 ✓/✗ 检查表,全部通过则退出码为 0, 任意一项失败则为 1。每一行失败信息都已经带上了路径和可操作的修复方法, 照着念就好,不用自己猜。--json 输出机器可读格式,方便接进其他工具; --quiet 只打印一行摘要,而不是完整表格。

$ /siltpoke-doctor

siltpoke-doctor — checking install health

  ✓  ~/.claude/settings.json valid
  ✓  Stop hook registered (http + command pair)
  ✓  ~/.siltpoke/inner.txt readable
  ✓  ~/.siltpoke/wake.json healthy (absent OK)
  ✓  ~/.siltpoke/global.json schema v3 current
  ✓  slash command symlinks intact (35/35)
  ✓  ~/.siltpoke/config.json valid
  ✓  last Brain call: ok @ 2026-07-01T14:32
  ✓  daemon running latest code

All 9 checks passed. Install healthy.

失败的行长这样,修复方法就在下面:

  ✗  slash command symlinks intact (34/35)
       siltpoke-tag-entities.md (missing). Run `bun src/cli/install.ts` to re-link.

常见修复

如何消掉每一条失败的 doctor 行——大多与 doctor 自带提示一致,有两条(schema、config)额外给了一步建议:

问题 修复方法
settings.json 缺失 重新运行安装器(bun src/cli/install.ts
Stop 钩子未配对 重新运行安装器——它是幂等的,可以放心重复运行
global.json schema 不匹配 可能是一次卡住的迁移——去 docs/handoff/ 查最近一次与 schema 相关的阶段
Symlinks 损坏 安装之后仓库挪动过位置——从新位置重新运行安装器
config.json 缺字段 重新运行 bun src/cli/first-run.ts(性格设置向导)

常见症状

  • 状态栏没有脸 —— 检查 ~/.claude/settings.jsonstatusLine 是否 指向 Siltpoke 的包装脚本,然后重启 Claude Code。
  • Brain 从不运行 —— 先运行 /siltpoke-stats 看今天的花费和触发模式, 再 tail -f ~/.siltpoke/brain-calls.jsonl,找 skipped 的原因: quiet_hoursbudget_hard_stopno_changewrong_event_modeon_demand_no_bypass

想立刻来一次评审?

/siltpoke-wake 是一次性绕过预算和安静时段两道闸门的方式——想让 Brain 按需运行、而不是等下一个门控事件时用它。