wheeler
CLAUDE.md12 starsChanged 3 months agoMITView on GitHub
Wheeler is a Python package that turns Claude Code into a provenance-tracked research assistant. It is not an agent framework: there is no orchestration layer. Claude Code is the orchestrator; Wheeler provides (a) MCP tools that mutate a Neo4j knowledge graph, and (b) /wh:* slash commands that act as mode-restricted system prompts. Everything runs locally on a Max subscription via claude -p subprocess. No API keys are used, ever. Version is 0.16.1. 3648 passing tests plus 3 optional Torch checks…
- Reads credentials
- Installs packages
In this repository47 other files it gives its agents.
- .claude/commands/wh/CLAUDE.mdCLAUDE.md
- docs/CLAUDE.mdCLAUDE.md
- wheeler/CLAUDE.mdCLAUDE.md
- wheeler-briefSkill
- wheeler-service-creatorSkill
- addSkill
- askSkill
- asta-assistantSkill
- asta-litSkill
- asta-reportSkill
- asta-scholarSkill
- astaSkill
- asta-theorizeSkill
- backupSkill
- bumpSkill
- chatSkill
- closeSkill
- compileSkill
- dev-feedbackSkill
- discussSkill
- dreamSkill
- executeSkill
- graph-linkSkill
- graph-reviewSkill
- handoffSkill
- ingestSkill
- initSkill
- lessonSkill
- llmsr-discoverSkill
- llmsr-transferSkill
- noteSkill
- pairSkill
- pauseSkill
- planSkill
- queueSkill
- reconveneSkill
- reportSkill
- restoreSkill
- resumeSkill
- serviceSkill
- skill-creatorSkill
- startSkill
- statusSkill
- triageSkill
- updateSkill
- wheeler-voiceSkill
- writeSkill
The file
We split this CLAUDE.md into the rules it gives an agent
8 rules. About 0 are common, 1 is distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 94
Every mutation (add_finding, link_nodes, set_tier, etc.) writes three places through wheeler/tools/graph_tools/__init__.py::execute_tool():
- Very specific: line 102
New mutation tools must route through
execute_tool(): Do not write directly to the backend or to files. - Very specific: line 104
If layers drift (graph deleted but file survives, or vice versa), graph_consistency_check detects it and repair=True reconciles using …
- Very specific: line 143
All share mcp_shared.py for trace ID generation, the @_logged decorator, config loading, and backend access.
- Very specific: line 155
Every node carries content_version (1 at creation, +1 on each update_node or set_tier) and content_hash.
- Very specific: line 257
Neo4j sessions don't allow concurrent queries: Never asyncio.gather inside a session.
- Distinctive: line 265
Never use em dashes: Use colons, commas, periods, or parentheses.
- Very specific: line 271
If .plans/STATE.md exists, read it.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this codebase is
Wheeler is a Python package that turns Claude Code into a provenance-tracked research assistant. It is not an agent framework: there is no orchestration layer. Claude Code is the orchestrator; Wheeler provides (a) MCP tools that mutate a Neo4j knowledge graph, and (b) /wh:* slash commands that act as mode-restricted system prompts. Everything runs locally on a Max subscription via claude -p subprocess. No API keys are used, ever.
Version is 0.16.1. 3648 passing tests plus 3 optional Torch checks (see the suite for the current count), 57 MCP tools across the four split servers. The legacy monolith has been removed.
Wheeler now runs on two agent hosts, Claude Code and OpenAI Codex, and ships as a plugin named wh rather than by copying files into ~/.claude/. The act bodies are served over MCP (get_act) so they exist in exactly one place; each host gets generated SKILL.md stubs. See "Multi-host distribution" below.
Commands
# Environment (Python 3.11+). uv is the primary workflow; the pip path
# remains supported for contributors without uv installed.
uv sync --extra dev # creates .venv/, installs from uv.lock
# or:
source .venv/bin/activate
pip install -e ".[test]" # core + test deps (fastembed + numpy are in core)
pip install -e ".[dev]" # full toolchain: tests + ruff + mypy + build
pip install -e ".[search]" # legacy no-op (fastembed+numpy already in core)
# Tests
python -m pytest tests/ -q # full suite, quiet
python -m pytest tests/test_merge.py -v # one file
python -m pytest tests/test_merge.py::TestExecuteMerge -v # one class
python -m pytest tests/ -k "consistency" # by keyword
python -m pytest tests/e2e/ -v # e2e, needs running Neo4j
python -m pytest tests/e2e/test_graph_first_acts.py -v # graph-first act workflows
python -m pytest tests/e2e/test_plan_lifecycle.py -v # plan lifecycle + triple-write
# E2E test philosophy: simulate the actual act workflow against live Neo4j.
# Each test walks through the exact tool-call sequence an act prescribes:
# 1. WRITE to graph (add_plan, ensure_artifact, add_finding, link_nodes)
# 2. READ back via the same query the act uses (query_plans, query_notes)
# 3. Verify graph state with direct Cypher (status, relationships, properties)
# 4. Verify triple-write artifacts on disk (knowledge/*.json, synthesis/*.md)
# This catches drift between what the act prompt says and what the graph
# actually stores. If an act says "call query_plans(status=approved)" but
# the query returns nothing because the status enum changed, the e2e test
# fails. Unit tests with FakeBackend can't catch that.
#
# TRAP when adding a live-Neo4j test late in the suite: graph_tools._get_backend()
# caches ONE module-level backend for the whole process, and each backend owns its
# own circuit breaker. Earlier tests push MagicMock values through that shared
# instance, which trips the breaker OPEN for 60s, so a live test placed after them
# fails with `circuit_open` while passing in isolation. Clear `_backend_instance`
# and call `invalidate_async_driver()` in your fixture's setup AND teardown.
# See tests/test_triple_write_project_root.py for the pattern.
#
# Also: the suite is sensitive to concurrent writers on one Neo4j. A red full-suite
# run in the asta/llmsr integration tests is worth re-running before treating it as
# a regression; those tests write and read back, so a second writer looks like a bug.
# Lint + type check (run by pre-commit hook)
.venv/bin/ruff check wheeler/
.venv/bin/mypy wheeler/ --ignore-missing-imports
# Git hooks (strongly recommended — block commits with API key leaks, broken tests, lint errors)
wh hooks install # copies .githooks/pre-commit + pre-push into .git/hooks/
wh hooks test # runs pre-commit checks without committing
# Headless Claude runs (for background tasks)
wh queue "prompt" # sonnet, 10 turns, structured JSON log to .logs/
wh quick "prompt" # haiku, 3 turns, one-shot
wh dream # graph consolidation (promotes tiers, detects communities, flags stale)
# MCP servers (launched by Claude Code via .mcp.json — not typically invoked by hand)
python -m wheeler.mcp_core # split: health/context/search/cypher/schema (14)
python -m wheeler.mcp_query # split: read-only query_* (11)
python -m wheeler.mcp_mutations # split: add_*, link, unlink, delete, merge, update, register_batch, lessons (22)
python -m wheeler.mcp_ops # split: staleness, citations, consistency, ops (10)The wheeler and wheeler-tools console scripts both point at the Typer CLI (wheeler.tools.cli:app): wheeler show F-3a2b, wheeler graph status, wheeler validate ..., wheeler install, etc.
Architecture (what you need to hold in your head)
The four-layer model
ACTS .claude/commands/wh/*.md slash commands = system prompts
FILE SYSTEM .notes/, .plans/, docs/ prose artifacts live as real files
SYNTHESIS synthesis/*.md Obsidian-compatible, auto-generated
GRAPH knowledge/*.json + Neo4j metadata + relationships onlyThe graph is an index over files, not a document store. A node stores id, type, tier, title (~100 chars), path, timestamps, and filterable metadata. Full content lives in knowledge/{id}.json. Human-browsable rendering lives in synthesis/{id}.md with YAML frontmatter and Obsidian [[backlinks]]. When a query needs content, it reads the JSON file; when it needs connections, it queries Neo4j.
Triple-write (load-bearing invariant)
Every mutation (add_finding, link_nodes, set_tier, etc.) writes three places through wheeler/tools/graph_tools/__init__.py::execute_tool():
- Neo4j graph node (via
GraphBackendABC) knowledge/{id}.json(atomic tmp-rename)synthesis/{id}.md(atomic tmp-rename, Obsidian-compatible; skipped whensynthesis_enabled: false, since nothing in Wheeler reads this layer back)
Plus an embedding in .wheeler/embeddings/ if search is enabled, plus a WriteReceipt in .wheeler/repair_queue.jsonl if any layer fails, plus a trace_id in .wheeler/request_log.jsonl for correlation.
New mutation tools must route through execute_tool(). Do not write directly to the backend or to files. The triple-write + receipt + trace_id + embedding wiring all lives in that dispatch path.
If layers drift (graph deleted but file survives, or vice versa), graph_consistency_check detects it and repair=True reconciles using whichever layer survived as the source of truth.
The strict layering
models.py, config.py <- zero internal deps (leaf nodes)
|
knowledge/store.py, render.py <- models only
|
graph/* <- models + config
|
provenance.py, merge.py,
consistency.py, contracts.py,
communities.py, write_receipt <- config ± models ± graph (layer 2-3)
|
tools/graph_tools/* <- graph + knowledge (lazy imports to knowledge/)
|
mcp_core/query/mutations/ops, <- everything
tools/cli.pyRules:
- Never add imports to
models.pyorconfig.py. They are the foundation. - Lazy imports in
tools/graph_tools/__init__.pyare intentional to break cycles withknowledge/andprovenance. Keep them lazy. - The split servers lazily import
consistency,communities,contracts,merge,search.retrieval,depscannerto keep server startup fast. Do not promote these to top-level. knowledge/migrate.pyis the one documented cross-layer exception: it imports fromgraph/at top level because it bridges the two by design.validation/ledger.pyhas a lazy upward import totools.graph_tools.execute_toolto persist ledger entries via the same triple-write path. This is the one real layering violation; keep it lazy.
See ARCHITECTURE.md for the full per-module dependency map.
The MCP tool surface (split servers are canonical)
The underlying implementation in wheeler/tools/graph_tools/ is exposed through four split FastMCP servers, one per role:
mcp_core.py— health, status, context, search, schema, raw cyphermcp_query.py— read-only typed listings (query_*)mcp_mutations.py— writes (add_*,link_nodes,update_node,set_tier, etc.)mcp_ops.py— validators, scanners, consistency (validate_citations, etc.)
All share mcp_shared.py for trace ID generation, the @_logged decorator, config loading, and backend access. The authoritative implementation of every tool lives in tools/graph_tools/, not in any mcp_*.py file. The server files are thin FastMCP wrappers that call execute_tool().
When you add a new MCP tool, register it in the split server that matches its role. Do not add it to any other server. The role map is: read-only listings → mcp_query; writes that change graph nodes → mcp_mutations; validators / scanners / consistency operations → mcp_ops; everything else (search, raw cypher, schema, health) → mcp_core.
The legacy wheeler/mcp_server.py monolith has been deleted (v0.14.0+). Its 50 tools were all
already covered by the four split servers, which carry 57. tests/test_mcp_surface.py now guards the
surface directly: per-server tool counts, no duplicate names across servers, every tool described,
and a test_monolith_is_gone check so it cannot be reintroduced. Each server also has its own test
file (tests/test_mcp_{core,query,mutations,ops,shared}.py).
Content versions and disclosure
Every node carries content_version (1 at creation, +1 on each update_node or
set_tier) and content_hash; the pre-change state is snapshotted to
knowledge/versions/<id>/v<n>.json. Edges record the endpoint versions they were
made against, citations may pin one ([F-3a2b@2], validated as outdated when
the node moved on), and show_node can read a version, answer
if_changed_since, and list one hop of neighbors with a moved flag. Listings
return pointers by default (WHEELER_DISCLOSURE=pointer, chosen by
evals/disclosure); show_node is the deep read.
See ARCHITECTURE.md "Content versions" and "Disclosure levels".
Provenance, stability, and staleness
Wheeler uses W3C PROV-DM relationships: USED, WAS_GENERATED_BY, WAS_DERIVED_FROM, WAS_INFORMED_BY, WAS_ATTRIBUTED_TO, WAS_ASSOCIATED_WITH, plus 8 Wheeler semantic relationships (SUPPORTS, CONTRADICTS, CITES, APPEARS_IN, RELEVANT_TO, AROSE_FROM, DEPENDS_ON, CONTAINS).
Every node carries a stability score (0.0–1.0) set by type and tier (Paper=0.9, primary Dataset=1.0, LLM-generated Finding=0.3, etc.). When a script's SHA-256 hash stops matching disk, detect_and_propagate_stale() in provenance.py traverses WAS_GENERATED_BY|USED*2..N transitively with exponential decay (default 0.8 per hop) and marks downstream nodes stale=true. The Cypher is entity-to-entity so max_edges = max_hops * 2 to account for each hop crossing WAS_GENERATED_BY and then USED.
Retrieval is multi-channel RRF
search_findings fuses four channels via Reciprocal Rank Fusion:
- Semantic (fastembed cosine on BAAI/bge-small-en-v1.5, stored in
.wheeler/embeddings/) - Keyword (graph substring queries over metadata)
- Temporal (recency boost via
updatedtimestamp) - Fulltext (Neo4j fulltext index on
_search_text, populated via triple-write in v0.6.0)
Any channel can be unavailable (no embeddings, no fulltext index, empty graph) and the survivors still produce results. search_context (v0.6.0) wraps this and expands each seed via 1-hop (all rels) + 2-hop (PROV only) so callers get provenance chains alongside matches.
Infrastructure hardening
Seven patterns you may see referenced across the code. The first six shipped in v0.6.0; transient retry was added later, alongside remote-Neo4j (Aura) support.
- Circuit breaker (
graph/circuit_breaker.py): 3-state breaker on Neo4j. After 3 consecutive failures it OPENs and fails fast in <1ms. After 60s it HALF_OPENs for a probe. Wraps every backend call. Never bypass. - Transient retry (
graph/driver.py::run_with_retry): retries transient Neo4j failures, which a remote database produces and a local one does not. Takes an async factory so each attempt opens its own session, which is how it honours the no-concurrent-queries rule rather than working around it. Composes with the breaker rather than sitting beside it: the breaker is checked before every attempt andCircuitOpenErrorpropagates instead of being retried. Classification is delegated tois_deterministic_neo4j_error()and the driver's ownis_retryable(), never duplicated.Neo4jBackendwraps it as_retry(safe to replay) and_once(must not be); seewheeler/graph/CLAUDE.mdfor the per-method decision table and the rule for a new method. - Consistency checker (
consistency.py): detects drift between graph ↔ JSON ↔ synthesis, repairs from surviving layer. - Trace IDs (
mcp_shared.py,request_log.py): every MCP call gets a uniquetrace_idthreaded through the log. Correlate multi-step operations by grouping on it. - Write receipts (
write_receipt.py):WriteReceipttracks which layers succeeded per triple-write. Incomplete writes go to.wheeler/repair_queue.jsonl. - Change log on nodes (
models.ChangeEntry):NodeBase.change_log: list[ChangeEntry]records field-level diffs onset_tierand invalidation propagation. Default is empty list for backward compat. - Task contracts (
contracts.py): handoff tasks declare required output nodes, required links, citation pass rate;validate_task_contractchecks them at reconvene.
Acts = slash commands = system prompts
Each .claude/commands/wh/*.md file is a Claude Code slash command. YAML frontmatter sets allowed-tools (which enforces per-mode tool access); the markdown body IS the system prompt. Mode enforcement (CHAT read-only, WRITE strict citations, EXECUTE full access) is entirely in the frontmatter.
wheeler/acts.py now reads these files at runtime (from the _data/commands/ mirror) and serves them over MCP via list_acts / get_act in mcp_core, so the bodies exist in exactly one place across both hosts. mode and orchestration are derived from allowed-tools rather than declared, so the two can never disagree: ops grant -> execute, mutations grant -> write, else chat; Agent/Task*/Team* -> subagents, Skill -> skill-dispatch. get_act(name, host=...) returns the body byte-identical plus a host-specific orchestration_note (Claude Code gets Agent/TeamCreate; Codex gets spawn_agent/send_input and is told the note supersedes any Claude tool the body names). _data/commands/ holds 41 .md files but CLAUDE.md is the authoring guide, so there are 40 acts.
/wh:start is a user-invoked router: it analyzes task intent and invokes the best /wh:* command via the Skill tool. Individual commands have narrow trigger descriptions requiring Wheeler/knowledge-graph vocabulary, so they auto-fire for unambiguous research actions but not for general coding.
The same command files exist twice: in .claude/commands/wh/ (what Claude Code actually loads from this repo) and in wheeler/_data/commands/ (what the PyPI package ships to users via wheeler install). Keep these two trees in sync. Edit the .claude/commands/wh/ tree, then run python -c "from wheeler.installer import sync_data; sync_data()" to regenerate the _data mirror byte-for-byte. Tests check both paths (tests/test_installer.py::test_package_data_in_sync, tests/test_context_routing.py, tests/test_routing.py::TestTreeSync).
The wheeler-brief skill (.claude/skills/wheeler-brief/, versioned with the repo via a .gitignore negation; other dev skills stay ignored) renders self-contained HTML research briefs (question, bulleted execution plan, data sources, figure mockups paired with actual results). It is invoked from /wh:plan after approval and /wh:execute after the completion summary; the act step is conditional ("if the skill is available, else skip silently") so shipped users without the skill are unaffected. The renderer is stdlib-only and unit-tested in tests/test_render_brief.py (which skips when the skill dir is absent).
Multi-host distribution: the wh plugin
Wheeler installs as a plugin on both Claude Code and Codex. wheeler install (writing into
~/.claude/) still works but is the legacy path.
/plugin marketplace add maxwellsdm1867/wheeler && /plugin install wh@wheeler # Claude Code
codex plugin marketplace add maxwellsdm1867/wheeler && codex plugin add wh@wheeler # Codexwheeler/build_plugin.py generates the whole tree from wheeler/_data/commands/ and
_data/agents/. Regenerate with python -m wheeler.build_plugin; never hand-edit the output.
tests/test_build_plugin.py::test_committed_tree_matches_generator is the drift guard. Emitted:
.claude-plugin/, .codex-plugin/, skills/<act>/SKILL.md (40), agents/, hooks/,
codex-profiles/, and .mcp-plugin.json.
Five host facts that are load-bearing, all established by canary against the real CLIs rather than by asking an agent (which confabulates its own tool list):
- The plugin MUST stay named
wh. Claude Code namespaces plugin skills as/<plugin>:<skill>, sowh+skills/plan/yields/wh:plan, unchanged for existing users. Skill dirs therefore strip thewh:prefix from the act'sname:. - Plugin MCP servers are namespaced too:
wheeler_coreinside pluginwhexposesmcp__plugin_wh_wheeler_core__<tool>, not the bare form. Sinceallowed-toolsis an allowlist matched on the full id, the generator emits both spellings for every grant. Copying an act'sallowed-toolsverbatim would deny every Wheeler tool and leave the skill inert while looking correct in review. allowed-toolssurvives in plugin skills, which is how CHAT/WRITE/EXECUTE enforcement keeps working on Claude Code. Codex accepts the key but does not enforce it, so Codex getscodex-profiles/*.config.tomlinstead, where mode is enforced by which servers are registered. Those profiles must writeenabled = falseexplicitly for out-of-mode servers: Codex merges profiles recursively, so omitting a server does not disable it.- Frontmatter must open the file. A leading HTML comment makes the whole block invisible,
silently dropping
descriptionandallowed-tools. The generated-file banner goes after it. - The legacy
~/.claude/commands/wh/*.mdtree fully shadows same-named plugin skills. Both installed at once means users silently keep the stale file copies.wheeler installnow refuses when the plugin is present,wheeler migrate-to-plugindoes the swap, andwheeler doctorreports the collision.
MCP servers launch via uvx --from wheeler==<version> wheeler-core-mcp, so there is no install
step: neither host can run a postinstall hook. Measured cost is 13 s once to populate the uv cache,
then ~370 ms per launch, which matches running the console script directly.
Service integrations (wheeler/integrations/) and adding a new service
External research tools land in the graph as provenance-tracked nodes via a sandwich: a /wh:<provider>-<tool> act reads graph context and shapes the request (--used source ids), the tool's own CLI runs (owning its auth and retries), and one deterministic marshal-out ingest module writes the result back through execute_tool (the only graph writer; lazy import, like validation/ledger.py). The split servers stay graph-side; integrations/asta/transport.py is the single subprocess boundary. AllenAI Asta is instance #1: four adapters (Paper Finder, Semantic Scholar, Theorizer, Literature Reports) routed by /wh:asta, read from the registry (integrations/registry.py: the bundled catalog services.default.yaml plus the .wheeler/services/ enabled folder curated by wheeler services enable/disable). Each call is one Execution with a TRUTHFUL status: the external-call failsafe (job_outcome gate + mark_execution_failed/mark_execution_completed + wheeler integrate record-failure) means a failed or partial job is recorded as failed with no fabricated outputs. Provenance is three parts: USED inputs, WAS_GENERATED_BY outputs (Papers excepted, they are reference entities), and the semantic edges wired in the act (judgment, not the parser). The Research Assistant is the LONG-HORIZON exception: its loop runs for hours and one harvest lands a whole batch, so it also (a) renders harvest.html on every ingest, (b) tags every node with custom_batch, and (c) marks each decision-bearing node custom_review_state=undiscussed instead of demanding a dozen endorsements inline. /wh:discuss <batch> reads that queue via query_review_queue and walks it with the scientist later. Any future batch adapter should reuse those three. See wheeler/integrations/asta/CLAUDE.md, ARCHITECTURE.md "Service Integrations", and docs/asta-engine-spec.md.
To add a new external service, use the wheeler-service-creator skill: do NOT hand-write an adapter. The skill (.claude/skills/wheeler-service-creator/) reads the codebase, asks the scientist the design decisions, and scaffolds the four pieces (registry contract, marshal-out ingest with the failsafe baked in, marshal-in act with the semantic-wiring + record-failure steps, and a live-Neo4j test stub) from a contract. Its bundled assets/audit_service.py auditor then checks data-safety (the e2e teardown deletes only by per-run e2e_tag), both provenance sides, the failsafe, and conventions. The loop is: run the creator -> capture one real output -> fill the parser -> audit -> adversarial review -> land. Hand-writing an adapter drops a provenance edge or the failsafe; the skill does not.
Non-obvious constraints
- Neo4j sessions don't allow concurrent queries. Never
asyncio.gatherinside a session; run sequentially. Backend helpers serialize inside a single session. - Community Edition isolation is simulated via a
_wheeler_projectproperty on every node. Whenconfig.neo4j.project_tagis set, allMATCH/CREATEqueries are namespace-scoped in Python. Enterprise/Aura users get real database isolation via the backend config. ANTHROPIC_API_KEYis actively unset bybin/whbefore every headless run. The pre-commit hook greps forANTHROPIC_API_KEY,api.anthropic.com,import anthropic,from anthropic import,anthropic.Anthropic(), andsk-ant-*patterns in staged content. Never add code that importsanthropic.- fastembed downloads a 33MB model on first use. This happens at
EmbeddingStore.__init__, not at package import.pip install wheelerdoes not trigger the download. - Graduated disclosure:
wheeler/,wheeler/graph/,wheeler/knowledge/,wheeler/tools/,wheeler/integrations/asta/,wheeler/integrations/llmsr/, and.claude/commands/wh/each have their ownCLAUDE.md. Read the relevant one before editing that subtree. They are the authoritative per-directory reference.
Style rules that apply to Wheeler-generated writing
- Never use em dashes. Use colons, commas, periods, or parentheses.
- Citations are strict in write/execute modes, flexible in chat/discuss/plan. Claims about our data cite
[F-xxxx], methods cite[P-xxxx], interpretations are marked, speculation is free. - Never do the scientist's thinking. Sharpen the question, flag sparse graph areas, ask questions rather than pad thin answers. Task routing: SCIENTIST does math/judgment, WHEELER does grinding/drafts, PAIR does walkthroughs.
On startup
If .plans/STATE.md exists, read it. Call graph_context for recent findings before substantial work.
See ARCHITECTURE.md for the complete technical spec (930+ lines: module dependency map, MCP tool listing per server, PROV schema, hardening patterns, design decisions).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

