Scarlet
To find what is hidden, hold a séance. To bind the names you find, give them to Scarlet.
Scarlet now ships inside khimaira as a workspace member at
packages/scarlet/. Its tools are re-registered on khimaira's MCP server under source-prefixed names (mcp__khimaira__scarlet_analyze_project, etc.). The standalonescarlet servepath below still works for backward compatibility.
Scarlet is a codebase cartographer — an MCP server that walks any project, extracts structural metadata via tree-sitter, and generates the documentation scaffolding AI assistants need to actually understand a feature-organized codebase: per-feature CLAUDE.md files, barrel exports (index.js/index.ts), Mermaid dependency graphs, and symbol manifests.
Built for Claude Code, but works with any MCP-compatible client.
The dark cousin of Séance: Séance retrieves knowledge from the dead code via vector search; Scarlet inscribes the names of every entity into a permanent record. Both now live inside the khimaira workspace.
Why "Scarlet"
Named for the Scarlet Woman of Revelation 17 — the figure who sits on a beast "full of names of blasphemy" with a name written on her forehead and a cup full of mysteries in her hand. She is defined by the inscribed names she bears. A catalog walking around in human form.
That maps directly onto what the tool does:
- Catalogs named entities (components, hooks, slices, types, interfaces) from a codebase
- Inscribes them into permanent records — per-feature
CLAUDE.mdfiles, barrel exports, symbol manifests - Surfaces what was hidden — gotchas, invariants, TODOs, deep imports that bypass barrels
Other scarlet threads in scripture reinforce the metaphor:
- Isaiah 1:18 — "though your sins be as scarlet…". Scarlet was the dye that couldn't be washed out; the stain that remained visible. Documentation fixes structure in the same way — makes the shape of a feature indelible so refactors can't silently erase it.
- Rahab's scarlet cord (Joshua 2:18) — a visible marker placed on the house that should be preserved when everything around it is destroyed. Every generated
CLAUDE.mdis a scarlet cord: a feature tied to an inscription so it survives refactors that would otherwise erase its shape.
There's also Hawthorne's Scarlet Letter — a public mark that fixes identity, making a person's defining characteristic impossible to ignore. Scarlet does the same to features: once their symbols are inscribed, their identity is legible to anyone (or anything) reading the record.
And it pairs with its sibling tool in the ritual:
To find what is hidden, hold a séance. To bind the names you find, give them to Scarlet.
Séance is the summoning. Scarlet is the scribe at the ritual — the one who takes down what comes through.
What it does
flowchart LR
A[Any Project] -->|claude mcp add| B[Scarlet MCP]
B --> C[Tools]
C --> D[analyze_project]
C --> E[scan_features]
C --> F[extract_feature_metadata]
C --> G[generate_barrel]
C --> H[build_claude_md]
C --> I[generate_dep_graph]
C --> J[lint_claude_md]
D & E & F & G & H & I & J --> L[Project gets:<br/>CLAUDE.md per feature<br/>Barrel exports<br/>Mermaid dep graph<br/>Lint reports]
The core principle
Deterministic parts run in Python. Judgment parts run in Claude.
| Generated by Scarlet (Python + tree-sitter) | Generated by Claude (synthesis) |
|---|---|
| Public API list | Vocabulary (domain terms) |
| Key files index | Conventions and invariants |
| Cross-feature consumers | Common task recipes |
| Dependency graph | Known issues and gotchas |
| Barrel exports | One-paragraph feature description |
| Symbol manifests | The why behind every "don't do X" |
The tools generate structure. Claude generates meaning. On every CLAUDE.md update, the auto-derivable sections refresh and the human/Claude-written sections are preserved using <!-- BEGIN MANUAL --> ... <!-- END MANUAL --> markers.
Indexing pipeline
flowchart TD
A[Project root] --> B[analyze_project]
B --> C{Detect framework, state mgmt,<br/>features root, package manager}
C --> D[scan_features]
D --> E[For each feature]
E --> F[Walk source files]
F --> G[Tree-sitter parse]
G --> H[Extract exports:<br/>components, hooks, slices,<br/>interfaces, types]
H --> I[Find consumers via import graph]
I --> J[Build skeleton from template]
J --> K{Existing CLAUDE.md?}
K -->|Yes| L[Preserve manual sections,<br/>refresh auto sections]
K -->|No| M[Write new skeleton with<br/>placeholder manual sections]
L --> N[Write CLAUDE.md]
M --> N
The CLAUDE.md template
Every generated CLAUDE.md follows the same shape — auto sections refresh on every update, manual sections persist:
- Description (manual) — one-paragraph summary
- Vocabulary (manual) — domain terms specific to this feature
- Public API (auto) — exported components, hooks, slices, types
- Key files (auto) — every source file with the symbols it defines
- Conventions and patterns (manual) — invariants, idioms, the why behind every rule
- Consumers (auto) — features that import from this one
- Common tasks (manual) — step-by-step recipes
- Known issues and gotchas (manual) — landmines, deprecated patterns
- See also (auto) — cross-references to study guides and rules
Installation
Prerequisites
- Python 3.12+
- uv package manager
Setup
git clone git@github.com:fsocietydisobey/scarlet.git
cd scarlet
uv sync
No API keys required — Scarlet is fully local and stateless.
CLI usage
# Scan a project — overview + feature table
uv run scarlet scan ~/work/jeevy_portal
# Generate or refresh one feature's CLAUDE.md
uv run scarlet describe ~/work/jeevy_portal drawings
uv run scarlet describe ~/work/jeevy_portal drawings --dry-run # preview only
# Refresh all feature CLAUDE.md files
uv run scarlet sync ~/work/jeevy_portal
# Generate a barrel export for a feature
uv run scarlet barrel ~/work/jeevy_portal drawings --ext js
# Generate a Mermaid dependency graph
uv run scarlet graph ~/work/jeevy_portal -o shared-docs/feature-deps.mmd
# Lint all CLAUDE.md files for staleness and missing sections
uv run scarlet lint ~/work/jeevy_portal
# Start the MCP server
uv run scarlet serve
MCP integration
Claude Code
Register globally so it's available in every project:
claude mcp add -s user scarlet -- uv --directory /path/to/scarlet run scarlet serve
Once registered, Claude gets these tools:
| Tool | What it does |
|---|---|
analyze_project |
Detect framework, state mgmt, package manager, feature count |
scan_features |
List all features with state (CLAUDE.md, barrel, counts) |
extract_feature_metadata |
Pull all exports from one feature |
generate_barrel |
Create a barrel export file for one feature |
build_claude_md |
Generate or refresh a feature's CLAUDE.md |
generate_dep_graph |
Output Mermaid or JSON dependency graph |
lint_claude_md |
Validate a feature's CLAUDE.md for staleness |
list_consumers |
Find all features that import from a given feature |
Other MCP clients
{
"mcpServers": {
"scarlet": {
"command": "uv",
"args": ["--directory", "/path/to/scarlet", "run", "scarlet", "serve"]
}
}
}
Configuration
Create a .scarlet.yml at the root of any project to declare conventions:
project_type: nextjs
state_management: redux-toolkit
test_framework: jest
features_root: frontend/src/features
study_guides_path: frontend/study-guides
barrel_export_strategy: re_export_default
This makes Scarlet reusable across projects with different shapes. Without .scarlet.yml, Scarlet uses sensible defaults and detects most things heuristically.
Architecture
graph TB
subgraph "src/scarlet/"
CLI[cli.py<br/>CLI entry point]
SRV[server.py<br/>MCP server]
CFG[config.py<br/>.scarlet.yml loader]
subgraph "analyzer/"
PR[project.py<br/>framework detection]
FT[features.py<br/>feature scanning]
MD[metadata.py<br/>tree-sitter export extraction]
IM[imports.py<br/>import graph walker]
end
subgraph "generator/"
BR[barrel.py<br/>index.js generator]
CM[claude_md.py<br/>CLAUDE.md generator]
DG[dep_graph.py<br/>Mermaid renderer]
TPL[template.py<br/>skeleton template]
end
subgraph "validator/"
LN[linter.py<br/>staleness checks]
end
end
CLI --> PR & FT & MD & BR & CM & DG & LN
SRV --> PR & FT & MD & BR & CM & DG & LN & IM
CM --> MD & IM & TPL
BR --> MD
DG --> IM
Composition with Séance and Serena
Scarlet is the writer. It produces structure for the read tools to consume:
flowchart BT
A[Source Code<br/>ground truth]
A --> B[Serena<br/>LSP - exact symbolic queries]
A --> C[Séance<br/>vector search - semantic queries]
B & C --> D[Scarlet<br/>analysis + writing]
D --> E[Generated Docs<br/>CLAUDE.md, barrels, dep graphs]
E --> F[Architecture MCP<br/>serves curated docs]
| Tool | Question it answers | Mode |
|---|---|---|
| Serena | "Where is symbol X defined? Who calls it?" | Read (LSP-precise) |
| Séance | "Find code related to concept X" | Read (semantic-fuzzy) |
| Scarlet | "Generate or refresh the structural docs" | Write |
| Architecture MCP | "What does the curated map say about X?" | Read (curated prose) |
The four together form a complete loop: Scarlet writes, the others read.
Tech stack
- tree-sitter — AST parsing for Python, TypeScript, JavaScript
- MCP Python SDK — Model Context Protocol server
- Click — CLI framework
- PyYAML — Config file parsing
- uv — Package management
No vector database, no API keys, no embeddings. Scarlet is fully local, deterministic, and stateless.
License
MIT
Metadata
Release files for khimaira-scarlet 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| khimaira_scarlet-0.2.0.tar.gz | 32.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| khimaira_scarlet-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 71.6 kB
Release files / khimaira_scarlet-0.2.0.tar.gz
| Download URL | khimaira_scarlet-0.2.0.tar.gz |
|---|---|
| Size | 32.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e8ed65ffd162b993e1c681affe5b074d4fe7ab58b119c9aae0afc050ee0a6b9
|
|
BLAKE2b-256 checksum How to use checksums |
f2f42239149f79fd0a3f60f4ea8b2d4882181a545a70845d2426bd546e849014
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / khimaira_scarlet-0.2.0-py3-none-any.whl
| Download URL | khimaira_scarlet-0.2.0-py3-none-any.whl |
|---|---|
| Size | 38.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
42ba64536ca4887cf58aa59cdf2ac618cc3c42bd5a1780aec2842db4c952c862
|
|
BLAKE2b-256 checksum How to use checksums |
b3ba5702acdf123ab1d38cd33c1a2ab3b7db1ec392256d91b01ceefb99ff3d95
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|