A `/backstory` command and tool that annotate each line with the git commit that last touched it and the agent turn + prompt that wrote it, from a persistent per-line ledger (drift-proof by content hash), DSH-* commit trailers, or the live session log.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-backstory
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:MeghanBao/dsh-backstory
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
English · 中文
Ask any line of code its backstory — what it does, and why it's here.

A DeepSeek Harness (dsh) plugin.
git blame tells you who wrote a line and when. dsh-backstory adds the part
that actually matters when you're staring at unfamiliar code: what it does and
why it exists — grounded in the commit that last touched it and in the agent's
own history: which turn wrote each line, and the prompt that triggered it.
backstory src/blame.ts:27
──────────────────────────────────────────────────────────────
L27 · a5d49e9 2026-08-20 — "feat: dsh-backstory v0.1 …"
const header = /^([0-9a-f]{40}) \d+ (\d+)(?: \d+)?$/.exec(raw)
→ WHAT: matches a `git blame --line-porcelain` header line (sha + line numbers)
→ WHY : commit "dsh-backstory v0.1" — starts a new blame record for each line
Why it's different
git blame→ who / when / which commit.dsh-backstory→ what the line does + why it's here, in one place.Not a generic "explain this code" (any LLM does that). The why comes from real repository history, so the answer is grounded, not guessed.
When the agent itself wrote a line, it adds a dsh-native
originthatgit blamecan never give you — which turn wrote it, and your prompt — per line (🧬t14) and for the file:L1 · a5d49e9 … 🧬t14 export const greeting_de = "Willkommen" 🧬 origin · turn 14 — you asked: "支持德语双语" [ledger-hash]
Provenance: three layers
Each queried line is attributed by whichever source is most precise, in order:
- Ledger content-hash (
[ledger-hash]) — every write/edit is recorded to a repo-committed.dsh/backstory.jsonlwith the touched lines' content hashes. A line is matched by its text, so it survives moving up/down the file (line-number drift). This persists across sessions, machines, and people. - Commit trailer (
[commit]) — once work is committed withDSH-Turn/DSH-Prompttrailers,git blame → sha → trailerrecovers the provenance and git's own line tracking handles drift for free. - Live session log (
[session]) — for the current session before anything is written to the ledger, reconstructed fromexec.agent.session.events.
All three degrade gracefully: no ledger, no trailers, no git — you still get the source lines back.
Install
Two ways to run the same engine.
As an MCP server — any MCP client (Claude Code, Cursor, …)
No DeepSeek Harness required. Point your client at the backstory-mcp binary,
which speaks the Model Context Protocol over stdio and exposes the backstory
and backstory_remember tools. For Claude Code:
claude mcp add backstory -- npx -y dsh-backstory
Or wire it into any client's MCP config directly:
{
"mcpServers": {
"backstory": { "command": "npx", "args": ["-y", "dsh-backstory"] }
}
}
Run it from the repo whose history you want to query — the server reads git and
the .dsh/ ledger relative to its working directory. The standalone server uses
the git-native provenance (commit trailers + the committed ledger); the live
per-turn session origin is exclusive to the dsh plugin below.
As a DeepSeek Harness plugin
dsh plugin add dsh-backstory
The dsh host applies the bundle patch declared in package.json
(dsh.bundle.patch → cordis.patch.yml), which inserts the
plugin into the running composition. No extra wiring needed.
From source (local development)
git clone https://github.com/MeghanBao/dsh-backstory.git
cd dsh-backstory
npm install
npm run typecheck # tsc --noEmit
npm test # blame parser, provenance engine, git-blame e2e
npm run build # emit the MCP server to dist/ (backstory-mcp bin)
npm run mcp # run the MCP server over stdio from source
The standalone cordis.yml loads just the dsh plugin for local
iteration.
Usage
Type the /backstory command, optionally with a file and line range:
/backstory src/auth.ts:40-60
/backstory utils/date.ts
Or just ask the agent in natural language (it uses the same backstory tool):
- "what's the backstory of
src/auth.tsline 88?" - "explain
utils/date.tslines 10–40 and why each part is there"
The tool returns each line with the commit that last touched it (author, date,
message) and — when known — the agent turn/prompt that wrote it (🧬t<turn>).
The agent narrates what the code does and uses the commit message + origin for
why. Outside a git repo it degrades gracefully to source-only.
Tool: backstory
| Param | Type | Notes |
|---|---|---|
path |
string (required) | absolute or workspace-relative |
line |
number | first line (1-based); omit for the whole file |
endLine |
number | last line; defaults to line |
Whole-file reads are bounded to 400 lines.
The ledger & commit trailers
The plugin records every write/edit to .dsh/backstory.jsonl automatically
(via a tools/post-execute observer) — commit that file to make provenance
travel with the repo.
To also anchor provenance in git history (drift handled by git), install the
prepare-commit-msg hook once per clone:
npm run install-hook
From then on every commit gets the newest ledger record for its staged files folded into trailers automatically:
DSH-Turn: 14
DSH-Prompt: 支持德语双语
DSH-Session: 0f3a…
The hook is best-effort (never blocks a commit), idempotent (safe on --amend),
and self-disabling if removed. It backs up any existing hook to *.backup.
Incremental explanations
Explaining a line costs a model call, so explanations are cached. After the agent
explains the unexplained lines from a backstory result, it calls
backstory_remember to store them — keyed by each line's content hash, in
.dsh/backstory-notes.jsonl. Next time, unchanged lines come back with their
explanation already attached (↳), and only lines whose text changed need
re-explaining. Cheap, and never stale.
Privacy: redaction & opt-out
Prompts are stored in the ledger (and, via the hook, in commit trailers), so
common secrets are scrubbed automatically before they are written — OpenAI /
GitHub / AWS / Slack / Google keys, JWTs, Bearer tokens, and key=value pairs
for password / token / secret / api_key become [REDACTED].
Turn recording off, or add your own patterns, via .dsh/backstory.config.json:
{ "record": true, "redactPatterns": ["ACME-\\d+"] }
Or disable it everywhere with an env var: DSH_BACKSTORY_DISABLE=1.
⚠️ Redaction is best-effort pattern matching, not a guarantee — review commits before pushing, and opt out for anything sensitive.
Roadmap
- v0.1 — git-history backstory: line → commit → what/why. ✅
- v0.2 — dsh-native half: reconstruct which agent turn wrote a file and the prompt that triggered it, from the live session log (file-level). ✅
- v0.3a — persistent line-level ledger: record every write/edit to
.dsh/backstory.jsonl(turn, prompt, touched lines, content hashes); survives across sessions/machines/people. ✅ - v0.3b — drift-proof attribution: match a line by its content hash, so provenance survives the line moving in the file. ✅
- v0.4 — git-native provenance:
DSH-*commit trailers, recovered viagit blame → sha → trailer, with drift handled by git itself; plus aprepare-commit-msghook installer (npm run install-hook) that folds ledger records into trailers automatically. ✅ - v0.5 — privacy: automatic secret redaction in stored prompts + a
.dsh/backstory.config.json/DSH_BACKSTORY_DISABLEopt-out. ✅ - v0.6 — a
/backstoryuser command (registered as a dsh skill) that drives the tool with a file:line argument. ✅ - v0.7 — incremental explanations: cache per-line explanations by content
hash (
backstory_remember→.dsh/backstory-notes.jsonl); only re-explain lines that changed. ✅ - v0.8 — standalone MCP server (
backstory-mcp): the same engine over the Model Context Protocol, so any MCP client (Claude Code, Cursor, …) gets thebackstory/backstory_remembertools with no dsh required. Reuses the git-native core; compiled todist/and published to npm. ✅
Status
Built against the dsh developer preview — APIs may shift. The blame parser,
provenance engine, ledger, hash attribution, git-blame and commit-trailer paths
are covered by 43 tests (pure logic + e2e against real temp repos). Every
runtime touchpoint (exec.agent.session.events, the tools/post-execute
recorder) is defensive and degrades gracefully, so the tool never breaks.
License
MIT © Meghan Bao
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-git-graph★ 8440
Git branch selector and Git graph for the dsh web GUI: switch branches and explore branch-lane and commit history from the conversation header.
Akimiya-z/codex-guard#dsh★ 138
Pre-submit pull-request hygiene checks inside DeepSeek Harness: scans the current diff for TODO leftovers, hardcoded secrets, and non-conventional commit subjects.
Cerbur/clutch-dsh#clutch-dsh-worktree★ 30
Adds a Worktree view to DSH Web UI that groups Sessions by Git worktree while keeping DSH as the source of truth.
lehhair/dsh-diff-viewer★ 26
PiUI-style diff viewer replacing the stock DiffBlock for write/edit tool calls.
DamonKoy/dsh-web-ui#dsh-git-graph★ 23
Git branch selector and Git graph in the conversation header of the dsh web GUI.
PerryLink/dsh-github★ 23
Official-grade GitHub CI integration: a composite action.yml, a polling PR review bot with idempotent inline comments and a status-check gate, plus PR/issues tools with every write gated by human approval.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.