nuget UnwrittenstdioMITupdated 1mo ago
Catch the files you — or your AI agent — forgot to change. Unwritten learns from your git history which files usually change together and warns when one is missing from your edit: "you changed OrderService.cs but not OrderServiceTests.cs, and they change together 94% of the time." It calls these missing companions holes, and every warning comes with its confidence score and real example commits as proof.
What can you do with Unwritten?
Unwritten
Catch the files you — or your AI agent — forgot to change. Unwritten learns
from your git history which files usually change together and warns when one is
missing from your edit: "you changed OrderService.cs but not
OrderServiceTests.cs, and they change together 94% of the time." It calls
these missing companions holes, and every warning comes with its confidence
score and real example commits as proof.
1 possible hole(s):
src/Orders/OrderServiceTests.cs
expected because you changed src/Orders/OrderService.cs
confidence 0.826 (90 co-changes in 100 changes)
e.g. 3f2a1c9 Add surcharge handling to freight calculation
FAIL: at least one hole at confidence >= 0.70.
It runs as an MCP server so AI coding agents (Claude Code, Copilot) can check
their own edits mid-session, and as a CLI for pre-commit hooks.
One dotnet tool execute, an index in .unwritten/, no server, no subscription,
no tokens.
Works on any language. File-level rules only need git history, so hole detection works the same on Python, TypeScript, Go, or mixed repos. C# repos additionally get method-level rules and cosmetic-edit filtering; JSON files get key-level noise filtering. Running the tool requires the .NET SDK (10+), but the repos it analyzes can be anything.
Quick start
From your repo's root:
- Warm up the index (optional — every command builds it on first use and keeps it current by itself; this just makes the first query fast).
dotnet tool execute Unwritten --yes -- reindex
- Register as an MCP server (Claude Code):
claude mcp add unwritten -- dotnet tool execute Unwritten --yes -- mcp
- (Recommended if using Claude Code) Make the check deterministic — a git pre-commit hook and a Claude Code Stop hook that feeds failing holes back to the agent before a commit is made:
dotnet tool execute Unwritten --yes -- install-hook --git --claude-code
That's it — your agent can now call check_holes after editing, and the hooks
catch the cases where it forgets to.
Explanatory video
https://github.com/user-attachments/assets/4ae7a22d-c611-458f-8c48-08f87eca1d29
See it in action
stats shows what the tool has learned — here, one file pair coupled strongly
enough (confidence ≥ 0.7) to block a commit if one side is missing:

After editing one file of that pair, check warns that its companion is
missing and spells out the three ways to resolve it — update the companion,
commit anyway, or mute the rule:

With the pre-commit hook installed, the same check runs automatically on every commit and blocks it while the companion is still missing:

Documentation
Everything else lives in docs/README.md:
- Background & the research behind it
- Why these thresholds (tested on real data)
- Use: MCP server, CLI, hooks, muting false rules
- Configuration reference
- How it works
- What it does NOT do (yet)
License
Install
Add Unwritten to your client. Pick the one you use.
claude mcp add unwritten -- dnx Unwritten --yescodex mcp add unwritten -- dnx Unwritten --yesamp mcp add unwritten -- dnx Unwritten --yes{
"mcpServers": {
"unwritten": {
"command": "dnx",
"args": [
"Unwritten",
"--yes"
]
}
}
}Add to `claude_desktop_config.json`, then restart Claude Desktop.
{
"mcpServers": {
"unwritten": {
"command": "dnx",
"args": [
"Unwritten",
"--yes"
]
}
}
}Add to `~/.cursor/mcp.json`, or `.cursor/mcp.json` for a single project.
code --add-mcp '{"name":"unwritten","command":"dnx","args":["Unwritten","--yes"]}'Or add the block manually to `.vscode/mcp.json` under `servers`.
{
"mcpServers": {
"unwritten": {
"command": "dnx",
"args": [
"Unwritten",
"--yes"
]
}
}
}Add to `~/.codeium/windsurf/mcp_config.json`.
{
"mcpServers": {
"unwritten": {
"command": "dnx",
"args": [
"Unwritten",
"--yes"
]
}
}
}Add to `cline_mcp_settings.json` via the MCP Servers panel.
{
"mcpServers": {
"unwritten": {
"command": "dnx",
"args": [
"Unwritten",
"--yes"
]
}
}
}Add to `~/.gemini/settings.json`.
{
"mcpServers": {
"unwritten": {
"type": "local",
"command": "dnx",
"args": [
"Unwritten",
"--yes"
],
"tools": [
"*"
]
}
}
}Add to `~/.copilot/mcp-config.json`, or run `/mcp add` inside the CLI.
{
"context_servers": {
"unwritten": {
"command": {
"path": "dnx",
"args": [
"Unwritten",
"--yes"
]
}
}
}
}Add to your Zed `settings.json`.
dnx Unwritten --yesRun `goose configure`, choose **Add Extension → Command-line Extension**, and paste this command.
Score
39 / 100
Incomplete
- Documentation25/25
- Maintenance25/25
- Trust13/20
- Capability0/15
- Install experience12/15
- Documents what it does and how to connect
- Has a resolvable package or endpoint
- Exposes at least one tool, prompt or resource
- README has substantive content
- Includes a code example
- Documents its configuration
- Mentions credentials or security posture
- Last commit 24 days ago
- Has a release history
- Repository is not archived
- Licensed MIT
- Namespace verified in the official MCP registry
- Claimed by its owner
- Published under an organisation
- 0 tool(s) documented
- Provides prompt templates
- Provides resources
- 12 documented install method(s)
- Published to a package registry
- Offers a hosted endpoint — no local install
Version history
| Versions | Published |
|---|---|
| 0.4.6Latest | Jul 29, 2026 |
| 0.4.5 | Jul 29, 2026 |
| 0.4.3 | Jul 13, 2026 |
| 0.4.2 | Jul 13, 2026 |
| 0.4.1 | Jul 13, 2026 |
| 0.4.0 | Jul 12, 2026 |
| 0.3.0 | Jul 12, 2026 |
| 0.2.1 | Jul 12, 2026 |
| 0.2.0 | Jul 10, 2026 |