Your AGENTS.md should grow from your scars, not from a template. A local-first Claude Code companion.
Project description
FrictionLog
Your AGENTS.md should grow from your scars, not from a template. A local-first Claude Code companion.
FrictionLog watches your Claude Code sessions and only records correction moments — the times you rolled back an edit, told the AI "no, do X instead," or renamed something it just created. Every week it writes you a markdown digest with your top recurring frictions, drafts AGENTS.md rules you can copy-paste, and flags rules that have gone 30 days without a single match — so your rule set can shrink as well as grow.
It does not analyze your code. It does not phone home. It does not tell you how to be a better engineer.
30-second install
# uv (recommended)
uv tool install frictionlog
# or pipx
pipx install frictionlog
Then, inside your Claude Code project:
cd path/to/your/project
fl init
That's it. Now use Claude Code normally. Seven days later, look in ~/.frictionlog/digest/ for your first weekly digest.
The five commands you actually use
| Command | What it does |
|---|---|
fl init |
Inject FrictionLog hooks into .claude/settings.local.json. Run once per project. |
fl recent |
List recent friction events, newest first. --days N to widen the window. |
fl pattern |
Show your Top-5 recurring friction categories over the last 30 days. |
fl pending |
Show events FrictionLog flagged but you haven't confirmed yet. |
fl digest |
Generate this week's digest now. fl digest open opens it. fl digest --force bypasses the 7-day cooldown. |
There are more (fl confirm, fl status, fl ack, fl share-stats, fl uninstall). Run fl --help to see them.
How it works (60 seconds)
fl initinjects two hooks into.claude/settings.local.json:PostToolUse— fires after every Edit / Write / Bash / MultiEdit.UserPromptSubmit— fires when you send Claude your next message.
- The PostToolUse hook records the tool call to a local SQLite, runs a fast heuristic ("did the user just undo something?"), and writes one line to stderr if it suspects a correction:
💡 [FrictionLog] 检测到可能纠正:撤销 try-except. 下条消息以 fl:y / fl:n 开头确认。 - Start your next message with
fl:yto confirm,fl:nto reject,fl:ito ignore. The prefix is stripped before Claude sees the message — you only "pay" 4 characters. - Every 7 days, the hook silently spawns a background worker that scans your AGENTS.md, matches recent confirmed frictions, and writes
~/.frictionlog/digest/YYYY-MM-DD.md. - Read the digest, run
fl ack. Connect 4 weeks in a row and you've hit the north-star.
The privacy contract (three rules, no asterisks)
- Nothing leaves your machine without your explicit command. No telemetry, no auto-upload, no "anonymous usage stats." The only thing that calls out is the LLM SDK — and only when you've put your own key in
~/.frictionlog/config.toml. - The only place data lives is
~/.frictionlog/. SQLite + markdown files. Uninstall = delete that folder (FrictionLog won't touch it for you). fl share-statsis the only way to share anything, and it just opensgist.github.com/newwith a pre-rendered markdown of counts only — no event summaries, no project paths. You copy. You paste. You publish. We never see it.
If you're worried about the LLM hop: don't configure an LLM key. The heuristic-only path still works — you just lose the "second-pass classification" and the weekly rule-draft generation. The digest still ships with your Top-5.
Where things live
~/.frictionlog/
├── db.sqlite ← events, digests, rules, share_history
├── config.toml ← your LLM key (you create this; FrictionLog doesn't)
├── digest/
│ └── 2026-05-24.md ← weekly digests
├── queue/
│ └── pending_llm.jsonl ← async LLM classification queue
└── logs/
└── 2026-05-18.log ← rolling 7-day hook log
Configuring an LLM key (optional)
Create ~/.frictionlog/config.toml:
[llm]
provider = "deepseek"
base_url = "https://api.deepseek.com"
api_key = "sk-..."
classify_model = "deepseek-chat"
draft_model = "deepseek-reasoner"
Any OpenAI-compatible provider works — just point base_url at it. The hook never calls the LLM directly; classification runs in an async batch worker so you never wait for it.
Without a key, FrictionLog runs heuristic-only:
- ✅ Events recorded
- ✅ Stderr nudge +
fl:y/nconfirmation works - ✅ Weekly digest still ships with Top-5
- ❌ No LLM second-pass classification (heuristic is the only judge)
- ❌ No LLM-generated rule drafts in the digest
Frequently asked
"Does this slow Claude Code down?" No. Hook function-body p90 is ~26ms; the OS process spawn adds ~300ms but that's invisible next to the tool call itself (1-5s). LLM calls happen in a detached background process.
"What if I have other hooks in .claude/settings.json?"
fl init writes to .claude/settings.local.json (which Claude Code gitignores) instead, so it doesn't touch the shared file. It also detects and merges around any existing hooks. fl uninstall only removes the blocks FrictionLog added.
"Does it work on Windows?" Yes. Windows is a first-class target. CI runs on macOS + Ubuntu + Windows × Python 3.11 + 3.12.
"What if my AGENTS.md is in a weird format?"
FrictionLog parses bullet (-, *, +) and numbered (1.) lists outside of code fences and headers. Anything else is skipped. If your rule list isn't structured that way, the "stale rules" section just stays empty — nothing breaks.
"What gets sent to my LLM provider?" Per event: heuristic category, tool name, a short sanitized tool-input summary (file basename only, never the path), and your next user message truncated to 300 chars. Per digest: the Top-5 category names, counts, and one example summary each. No file contents. No project paths.
Uninstall
fl uninstall # removes the hook block from .claude/settings.local.json
rm -rf ~/.frictionlog/ # optional — your data and digests live here
uv tool uninstall frictionlog
fl uninstall deliberately leaves ~/.frictionlog/ alone. If you want to come back later, your event history is still there.
Status
Current version: v0.1.0 — MVP launch. Tested on Python 3.11 / 3.12 across macOS / Ubuntu / Windows. 297 tests in CI.
Roadmap:
| Milestone | Date | What |
|---|---|---|
| M4 (current) | 2026-07-09 | v0.1.0 public on PyPI |
| M5 | 2026-10-09 | 3-month north-star review |
| v0.2 | TBD | Cursor + Codex CLI adapters, web view |
Contributing
See docs/CONTRIBUTING.md. Bugs and feature requests welcome at GitHub Issues.
For deeper details: docs/PRIVACY.md (the long version of the privacy contract), docs/TROUBLESHOOTING.md (hook not firing, sqlite locked, LLM config), docs/CHANGELOG.md (version history).
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file frictionlog-0.1.0.tar.gz.
File metadata
- Download URL: frictionlog-0.1.0.tar.gz
- Upload date:
- Size: 199.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f629a80ebd187e93083b2fc47246ff54d74a1ac932f29cf6074e89101173934a
|
|
| MD5 |
f3c8434f945c1e16a950e0af29dba635
|
|
| BLAKE2b-256 |
30a91d92d995f9e5b9d350643a9914a55053e6a13bbfda3533bb2fc588d02c79
|
File details
Details for the file frictionlog-0.1.0-py3-none-any.whl.
File metadata
- Download URL: frictionlog-0.1.0-py3-none-any.whl
- Upload date:
- Size: 67.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
463079035260f60d38b72adc488a54dde4e3d95afcfcb6eaa9d5573ac3cf19e9
|
|
| MD5 |
535b810c0b0bd85434bb2e6aa1a5d843
|
|
| BLAKE2b-256 |
d6b1670eeb70cd106e2d1de8bd4aa03672d79d6cc3cca95250cf9c33b7ebf011
|