session-explorer
CLAUDE.md0 starsChanged 5 months agoMITView on GitHub
Implemented and released, installable from the Claude Code marketplace. The hook, manifest, index core, Textual TUI, --gc retention, uninstall, launchers, the live-session indicator, the split-pane tmux interaction layer (explorer left / active session docked right, F9 switch, F12 fullscreen), new-session creation (c), and CI are all shipped. SPEC.md is the authoritative source for architecture, data model, TUI behavior, install layout, and edge-case decisions — read it before proposing or writing any code. If a change would contradict the spec, update…
- Installs packages
- Commits and pushes
In this repositoryOne other file it gives its agents.
- cutting-a-releaseSkill
The file
We split this CLAUDE.md into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 109
All milestones (M1–M7) are shipped as of v1.5.0.
≈ “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.
Status
Implemented and released, installable from the Claude Code marketplace. The hook, manifest, index core, Textual TUI, --gc retention, uninstall, launchers, the live-session indicator, the split-pane tmux interaction layer (explorer left / active session docked right, F9 switch, F12 fullscreen), new-session creation (c), and CI are all shipped. SPEC.md is the authoritative source for architecture, data model, TUI behavior, install layout, and edge-case decisions — read it before proposing or writing any code. If a change would contradict the spec, update the spec in the same change rather than silently diverging.
What this project is
session-explorer — a Claude Code plugin that turns the JSONL transcripts under ~/.claude/projects/ into a file-explorer-style tree. Single entry point:
/session-explorer:openspawns a new terminal window running a Textual TUI. (Plugin commands are namespaced as<plugin>:<command>; the prefix is unavoidable.)- The TUI is the entire UX: browse, rename, move between folders, delete, edit notes, resume. No other slash commands.
- Index sidecar at
~/.claude/session-explorer-index.jsoncaches per-session metadata. User-created empty folders and per-project folder paths live in the separate folder store at~/.claude/session-explorer-folders.json. SessionStarthook records new sessions and refreshes the cache. It never modifies settings.json; retention is opt-in (see below).
Load-bearing design decisions
These are the constraints to preserve — violating any breaks the spec's contract:
- The user's session name is the only "tag". The plugin does not maintain a parallel tag field.
/rename(orclaude -n) is the single source of truth — both write acustom-titleevent to the JSONL. - A live session re-emits its
custom-titleevery turn, so "last-title-wins" alone reverts external renames. After the explorer renames a running session, Claude's next re-emit appends the OLD title as the JSONL's last line.index.set_nametherefore records superseded titles inname_shadows[], andrecord_sessionignores a shadowed last-title (keeping the user'sname_cached). A new, unshadowed title is still adopted, so an in-session/renamestill flows through. Don't restore naïve last-wins inrecord_session, and route all explorer renames throughindex.set_name(not a barename_cachedmutate). Seejsonl.all_custom_titles. ai-titleis NOT a name. Claude emitsai-titleevents automatically as it summarises an active session; those auto-generated titles are intentionally ignored byjsonl.session_name(). Onlycustom-titlecounts. Don't reintroduce the ai-title fallback.- "Kept" is implicit — a session is kept iff it has a (custom-title) name. Don't reintroduce a separate
keptflag. - Unnamed sessions are hidden from the TUI by default. Press
uto surface them for renaming or deletion. The index still tracks them (so--gccan expire stubs on schedule); only the default view filters them out. Don't conflate "hidden" with "deleted" — these are orthogonal. - Worktree sessions group under the parent repo. A cwd of
<repo>/.claude/worktrees/<name>derivesproject_labelfrom<repo>, not the worktree leaf — otherwise every worktree becomes its own top-level "project".project_pathkeeps the worktree path so resume chdir's into the correct working tree. Seeindex._project_label. - Worktree cleanup is non-destructive and explorer-owned. Removal goes through
worktree.remove()(bin/_pkg/worktree.py) usinggit worktree removewithout--force(git refuses dirty/untracked trees — never force it) and never deletes theworktree-<name>branch, so a removed worktree is just a "dead" one that_recreate_worktreerebuilds on resume. Exception (v1.18.0): permanent session deletion has nothing left to rebuild, soworktree.purge()(a separate primitive) removes the dir and safe-deletes the branch withgit branch -d(merged-only — an unmerged branch or a dirty tree is kept, never-D/--force).worktree.remove()itself is unchanged and still keeps the branch for thew/gc reclaim paths.delete_session(d) andgc.collect_garbage(--gc) both callpurgeand both drop the session's summary sidecar entry. The explorer owns this because Claude only offers native cleanup from the-w-creating process, and our retentioncleanupPeriodDays = 36500disables Claude's age-based sweep. Three triggers share the primitive:w(action_remove_worktree), the docked-exit offer in_poll_live(_maybe_offer_worktree_cleanup, once-per-sid via_offered_cleanup), andgc.collect_worktrees(idle >14d, clean, not live).collect_worktreesdoes NOT mutate the index and may reclaim directories of kept sessions — transcript + branch survive, so it's safe. The worktree git/FS primitives live inworktree.pysogc.pyneedn't import Textual. - Slash splits folder path from display name.
team/planning/sprint14→ folder pathteam/planning, displaysprint14. Multiple/create nested folders. Dashes are literal characters with no special meaning. Empty segments are dropped. - Folder structure lives in
~/.claude/session-explorer-folders.json, scoped per-project. Sessions named with/auto-add their path to the store on indexing. Pre-created empty folders live there too. The session index file no longer carries afolders[]field; a one-shot v1→v2 migration moves any legacy entries under a synthetic(unfiled)project. - Don't move or rewrite native JSONLs. Sessions stay where Claude Code wrote them so
/resumekeeps working. The only legitimate write to a JSONL is appending a rename event in the same shape Claude's own/renamewrites. - Retention is opt-in (modifying
cleanupPeriodDaysis the review-sensitive bit). Neither the hook norinstall.shtouches settings.json. The TUI asks on first launch (tui.on_mount→retention.enable/decline); only on "yes" is the priorcleanupPeriodDaysbacked up to~/.claude/.session-explorer.backupand set to36500. The backup's existence is the "retention enabled" signal; a.session-explorer.retention-declinedmarker records a "no" so the prompt isn't re-shown. --gconly runs once retention is enabled. The plugin's--gcdoes retention work, gated onname_cached IS NULL; the hook's once-daily auto-trigger is additionally gated on the backup file existing.uninstallrestorescleanupPeriodDaysfrom the backup.- Concurrent index writes (two Claude sessions starting at once). Use
flock+ temp-file-rename for every write. --gcskips live sessions. Skip any JSONL with an active flock ormtimewithin 60s.- Hooks never block startup.
SessionStartlogs failures to~/.claude/session-explorer.logand exits 0. - The explorer's own death must be visible and self-healing.
_run_applogs every crash traceback to~/.claude/session-explorer.log— including the ones Textual swallows: an exception in a message handler /set_interval/set_timercallback / worker never escapesapp.run(), Textual just setsreturn_code = 1and returns normally, so_run_appinspectsreturn_code/_exceptionafter a clean return too, andrun()propagates that non-zero code instead of running the execvp hand-off (v1.19.3 — returning 0 there closed theremain-on-exit=failedpane, wiped the on-screen traceback and ceded the window to the docked claude, leaving a crash with no evidence anywhere). Don't makerun()return 0 on a crash; the TUI marks its own paneremain-on-exit failedat mount (crash → dead pane with traceback on screen, never a claude-swallowed window; clean exit still closes); the launcher respawns a dead explorer pane beforenew-session -A. The periodic thread workers (live-meta, usage) call guarded_*_tickbodies —@workdefaults to exit_on_error=True andcall_from_threadre-raises UI-side exceptions in the worker, so an unguarded periodic tick failure kills the entire app. Don't add a new@workworker without the same guard, and don't "simplify" the tick wrappers away. - User text is DATA, never markup — escape it at every render site. Session
names, notes, summaries, first prompts, branches, paths, dialog strings and
search terms all reach the screen through markup templates, and Textual emits
a
[run it can't tokenize as literal text — which swallows the]of the next real tag and leaves the template's trailing[/]s with nothing to close (MarkupErrorout ofStatic.update, inside an interval callback → the app dies; that is howfeature/51010-[ADMIN] …killed the explorer — fixed v1.19.4). Route every user-derived value throughmarkup.escape(bin/_pkg/markup.py, Textual-free sosearch.pycan share it). It escapes every[: don't "simplify" it torich.markup.escape/textual.markup.escape, which only cover[a-z#/@]-initial tags and let[ADMIN]through — Rich ignores that too, but Textual parses it as a tag, and_preview_textis rendered by both engines (Static.update→ Textual,_render_live_preview→Text.from_markup). Escape LAST, after truncation and column padding: the backslashes render as zero cells, so widths computed on the raw text stay aligned, and truncating escaped text can cut a\[in half. - A crashed explorer is not a claude-swallowed impostor. v1.19.3's
remain-on-exit=failedleaves a dead TUI pane in theexplorerwindow so the traceback stays on screen and the launcher respawns it in place — but that window has no live TUI pane, which is preciselyheal_explorer_impostors's test for a window a docked claude took over. Renaming it aside deletes theexplorer:explorertarget the respawn needs and strands the docked claude under a name the tree can't map ("this session is already running in another terminal").heal_explorer_impostorstherefore skips any window containing a dead pane — only the TUI pane can be dead, since claude panes keep the defaultremain-on-exit=off— and derives the sid by scanning every pane, neverpanes[0](after a crash that's the dead TUI, which is what producedorphan-<pid>window names). Don't reintroduce a live-TUI-only test here. - TUI runs in a separate terminal, not inside Claude Code. Claude holds the terminal in raw mode; an interactive TUI must have its own TTY. Slash command spawns it via OS-detected launcher (
osascripton macOS;$TERMINAL/x-terminal-emulator/ known emulators on Linux). - One Python dep: vendored Textual. Bundled under
bin/_pkg/_vendor/. Nopip installruns on either install path. Don't add other deps casually. - Don't sum
input_tokens/output_tokensfrom the JSONL for context-size stats. Those are streaming-time estimates and have been observed to be order-of-magnitude wrong. Usecache_read_input_tokensfrom the latest assistant message; fall back tobytes / 4when caching wasn't active. UI labels the value with~to set expectations. - No in-place
/compact. v1 surfaces context size only; compaction stays a manual/compactinside a resumed Claude session. Don't reintroduce SDK-driven compaction without revisiting the spec. - Session summaries shell out to
claude -p, guarded so they leave no trace (v1.18.0).summarize.run()(bin/_pkg/summarize.py) spawnsclaude -p(Haiku default) withSESSION_EXPLORER_SUMMARIZER=1andSESSION_EXPLORER_PROBE=1in the env, sohooks/session-start.shbails at its early-exit guard and the summariser session records no index row / current-pointer / gc. Summaries live in a~/.claude/session-explorer-summaries.jsonsidecar (summary.py, flock + temp-rename like the other stores) — never in the index (it's rewritten every ~2s during live refresh). The auto-on-exit worker (_maybe_summarize→_summarize_worker) is a guarded@work(log-and-skip on failure, same rule as_live_meta_tick— an unguarded worker exception exits the app)._populatemerges summary text into the in-memory row dicts so_matches(the/filter) and_preview_textsee it without touching disk. Auto-summarise defaults off and is a toggle (marker.session-explorer.summaries-auto), not a permanent decision — a first-run prompt (marker.session-explorer.summaries-prompted, and envSESSION_EXPLORER_SUMMARY_NO_PROMPTsuppresses it in CI/tests) introduces it, and the Settings screen flips it forever after. Onlycustom-title-named sessions with ≥SUMMARY_MIN_MSGS(8) are auto-summarised.uworks on a live session too (v1.19.0) —build_digest/jsonl._iter_messagestolerate a mid-write transcript (partial last line is skipped), so there's no "stop it first" refusal; a summary made while the session is live is stored withprovisional: trueand is repeatable (pressuagain for a fresh snapshot). On exit,_maybe_summarize(ended)iterates the just-stopped sids and regenerates any that carry a provisional summary — to a final one, bypassing the auto-toggle and the length threshold (the user explicitly asked for it) — in addition to the docked session when auto-summaries is enabled._start_summarize/_summarize_worker/_summarize_tickthread aprovisionalflag into the sidecar entry;_refresh_previewmerges the sidecar summary +provisional+summarizingflags before the live-preview branch so a running session's preview shows its latest snapshot, the "live snapshot — refreshes on exit" tag (_summary_header), and the "⏳ Summarising…" indicator. - The preview pane is a bottom pane, and Settings is the home for persisted prefs (v1.18.0).
compose()stacks the preview inside thetreepaneVertical(between tree and queues), not in a right-sideHorizontal— the explorer is the left tmux pane, so a right-side preview was squeezed against a docked session.Spacestill toggles it. TheSettingsScreen(,) is the single surface for persisted toggles — auto-summaries, retention (with a configurable period inui_state.retention_days, read bycollect_garbagewhen--retention-daysis omitted), usage bar (mirrorsg), queues pane (mirrorsq) — plus a status/set-up-only tmux row (never a disable; re-surfaces the install offer after a decline). Row rendering/activation live on the app (_settings_rows/_settings_activate, unit-tested);retention.disable()restores the backed-upcleanupPeriodDays. Keepu/,in sync acrossBINDINGS,check_action, and_help_text. - Resume is non-destructive when tmux-hosted. The explorer is the left pane of the
explorerwindow and stays alive; the active session docks as the right pane (join-pane), while inactive sessions keep running as background windows. F9 switches focus between the two panes (also mouse-click); F12 zooms the focused pane fullscreen. Without tmux it falls back toexecvp. Don't reintroduce unconditional exit-on-resume, and don't reintroduce the old window-flipping/window-tab model (the explorer tree is the only session switcher). - tmux is an optional, consented dependency. Detect + offer install (declined-marker at
~/.claude/.session-explorer.tmux-declined), never bundle a binary, never silent-sudo. The dedicated-L session-explorerserver never touches the user's tmux. - Snapshots are read-only.
capture-panefor our tmux windows, transcript-tail otherwise. No embedded interactive terminal widget. - Full-text search (
f) is a live scan, not an index (v1.19.0).search.py(Textual-free, likeroot_guard.py/gc.py) reads the current project's JSONL bodies at search time — user+assistant message text only (same extraction shape assummary.build_digest), tool output/thinking/system dropped. There is no fourth sidecar; don't add one and don't fold body search into_matches(the/filter stays metadata-only — name/notes/first-prompt/summary). Scope is current-project (grouped bytree_model.session_root, so worktrees collapse in), not cross-project. The scan runs in a guarded worker (SearchScreen._run_searchlogs-and-skips — an unguarded worker exception exits the app, same rule as_summarize_tick).fselection is non-destructive (moves the tree cursor via_pending_select_sid, never resumes). Rename SearchScreen's result-render method away from_render— that name is a reserved Textual widget hook. Keepfin sync acrossBINDINGS,check_action(modal-up guard +/-filter-focus guard), and_help_text. - Sessions persist by default; only an explicit shut-down kills the server (v1.15.0, reverses "Option C"). The generated tmux config has no
client-detachedkill hook, so detaching the client by any means (red-button/Cmd+W, crash, or the deliberate[b]leave-running quit) leaves the server and all background sessions running; the next/openreattaches vianew-session -A. Onlyx → [s]("shut down all") runskill-server. The persist-flag mechanism (marker file +set/clear/persist_flag_set) was removed. Don't reintroduce a detach-time kill. - A new session's launch failure is captured, not silent. New-session windows run
exec claude … 2>a per-sid errfile; ~1.5s after launch_check_launch(tui.py) verifies the session is alive and, on death (typicallyclaude -wfailing to create its worktree), surfaces+logs the captured stderr and stamps the index row'slast_launch_error(shown in the preview). The docked-pane liveness arm verifies the pane actually exists — a claude that docks then dies within the delay window leaves_docked_sidas a phantom, which must read as dead (and be cleared), not alive. A transcript-less stub is started fresh on Enter via--session-id(_start_stub_fresh), never--resume.record_sessionclearslast_launch_erroronce a transcript appears. transcript_pathin the index can be dangling — check the disk before writing/resuming. The hook records the path at SessionStart, but claude creates the file (and its project dir) only on the first message. Rename/move/folder-cascade append the JSONL event only when the file exists (tui._transcript_on_disk); otherwise the rename is index-only, andindex.set_nameshadows the replacedname_cacheddirectly so claude's first write (re-emitting its-ntitle) can't revert it. Enter's stub test is likewise "no messages AND no transcript file on disk" (never--resumea dangling path), but a running transcript-less session docks instead of restarting. Don't reintroduceO_CREAT-ing claude's transcript before claude does — the missing project dir made it crash the TUI mid-callback, and the dead pane let the docked claude swallow the explorer window.- Claude Code RELOCATES a worktree session's transcript when the worktree is gone — the explorer must follow it (v1.19.1). As of Claude Code ~2.1.x, resuming (via native
/resume) a session whose worktree was removed moves the JSONL out of~/.claude/projects/<encoded-worktree-cwd>/into the parent repo's project dir and writes a{"type":"relocated","relocatedCwd":...}line. This breaks the old "branch + transcript kept; resume rebuilds it" assumption: the index row'stranscript_path/project_pathdangle and the session drops out of the tree though its data is intact.index.reconcile_relocatedre-links any dangling row to the moved<sid>.jsonlat its true cwd — read cwd viajsonl.effective_cwd(lastrelocatedCwd, NOT the dead worktreecwdthatsession_cwdreturns), and never decode the project-dir name (magento-os→magento/osis wrong). It merges onto the existing row (notes/name_shadows/created_atsurvive), unlike an F5refresh_allprune which deletes the row first. Runs fromon_mount, aRECONCILE_POLL_INTERVALguarded worker (_reconcile_relocated_tick, same log-and-skip rule as_live_meta_tick), and at the top ofreindex(so the prune keeps the healed row). Only scans disk when a tracked row is actually dangling; skips live sids and transcript-less stubs. - A relocated worktree session must RE-ISOLATE on resume, not run in the root (v1.19.2). A relocated session's
project_pathis the shared root, so a plainclaude --resumewould run it in the root — whereroot_guard._is_root_locationclassifies it as a guard-exempt "root owner", so it touches the shared installed root without a lease and its dirt makes every worktree agent's lease acquisition refuse (blocks the queue). Fix:reconcile_relocatedalso storesworktree_leaf(viajsonl.worktree_origin, recovered from the transcript's pre-relocation/.claude/worktrees/<leaf>cwds — survives relocation).action_resume/_exit_to_resumedetect a relocated worktree-born row (_relocated_worktree_leaf:worktree_leafset ANDproject_pathno longer underworktree.MARKER) and resume viaclaude -w <leaf> --resume=<sid>launched from the root (so--resumefinds the root-key transcript;-wrebuilds the worktree on its keptworktree-<leaf>branch and re-enters it). Empirically proven (realclaude 2.1.199, throwaway repo):-w --resumerecreates the worktree, the session's tool cwd AND the SessionStart-recorded cwd both become the worktree →root_guardtreats it as a worktree session (mustqueue-run). Threaded throughtmux.build_start_window(worktree=),_dock(worktree=),_resume_argv(worktree=). The explorer NEVER moves a JSONL — Claude owns the relocation. Don't reintroduce a plain in-root resume for aworktree_leafrow. - Shared-resource queue keys by git-common-dir, not
project_root. The Phase-1 lease engine (queue-run/queue-status/queue-cancel,bin/_pkg/queue_*.py+project_id.py+qsync.py+exclusive.py+probes.py) identifies a project byproject_id.project_id(cwd)(a hash ofgit rev-parse --git-common-dir), so every worktree of a repo collapses to one queue identity —index.project_root()(a/.claude/worktrees/string-strip) is NOT used for queues. The queue is the set of ticket files; the holder is the lowest-numbered ticket whose owner still holds its lifetimefcntl.flock(no daemon, crash-safe). Thesyncacquire strategy runsrsync -a --deletewith anchored--filter=excluderules and refuses the first sandbox transition until untracked/ignored would-delete paths are classified intoprotect/allow_delete. Never reintroduce--delete-excludedor rsyncP/protectrules. SeeSPEC.md→ "Shared-resource lease engine". - Phase-3 enforcement is location-based (fail-closed semantics, fail-open plumbing). For a Claude session in a worktree, the shared installed root is unreachable through tools (
Bash|Edit|Write|NotebookEdit) except via a singlesession-explorer queue-*command. Decision logic lives inbin/_pkg/root_guard.py(pure, Textual-free, unit-tested);hooks/pre-tool-use.sh→queue-guardis the hook shell, unchanged. Plumbing fails open (a broken hook emits nothing and exits 0); within working plumbing a root mention from a worktree session is denied with the exactqueue-runrewrite. The matcher widens fromBashtoBash|Edit|Write|NotebookEditin.claude-plugin/plugin.jsonandinstall.sh, torn down inuninstall.py(_HOOK_MARKERS/_HOOK_EVENTS) — keep all three in sync. SessionStart awareness (queue_awareness._render_context) shrinks to a ≤6-line usage hint. Deleted:guard_match.py({exe, sub}guard vocabulary),queue_detect.py(mtime out-of-lease toast). Per-project setup is a parameterlessstoggle (v1.17.1): no dialog —action_resource_setuppushes aConfirmScreenthat either stops sharing (current overlay resource) or enables/migrates one (writing the overlay shape under the existing id orroot). The Protect box was removed (it only fed the engine'ssyncstrategy, which is not a UI surface) and with it thectrl+d-swallowingTextArea;SharedRootScreen/QueueHelpScreen/_queue_help_text/parse_path_linesare deleted. Experimental claim: enforced for Claude tool calls, advisory beyond them.
Commands
- Tests (Python):
python3 -m pytest test/ -q. Single file:python3 -m pytest test/test_gc.py -q. Single test:python3 -m pytest test/test_gc.py::test_old_unnamed_session_is_deleted -q. Config inpytest.ini(asyncio_mode = auto); dev deps intest/requirements-dev.txt(pytest + pytest-asyncio). Textual is vendored, so nothing else needs installing. - Tests (shell):
bats test/install.bats test/uninstall.bats test/hook.bats— shell-level coverage ofinstall.sh,uninstall.sh, and the hook. (CLI subcommands are covered by pytest via subprocess intest_cli.py, so bats deliberately doesn't re-test them.) - CI:
.github/workflows/ci.ymlruns both suites on ubuntu + macos across Python 3.11–3.13 for every push/PR tomain. - Install (dev, plain path):
./install.shregisters the hook in~/.claude/settings.jsonand symlinksbin/session-explorerto~/.local/bin/. It does NOT touchcleanupPeriodDays(retention is opt-in via the TUI prompt). - Install (marketplace path):
/plugin marketplace add <this-repo>then/plugin install session-explorer. The plugin'sbin/is on the Bash-tool PATH automatically. - Uninstall:
./uninstall.sh(plain) orsession-explorer uninstall(marketplace, since/plugin uninstallhas no teardown hook). Both restorecleanupPeriodDaysfrom the backup. - Releasing: every iteration of changes that ships needs a version bump and a GitHub release. Follow the
cutting-a-releaseskill (.claude/skills/cutting-a-release/SKILL.md) — it is the authoritative checklist (bump__init__.py+plugin.json, update README/SPEC status lines and the help-screen keybindings if they changed, add aCHANGELOG.mdsection, thengh release create vX.Y.Z). Don't ship version-affecting work without running through it.
Implementation order
All milestones (M1–M7) are shipped as of v1.5.0; the SPEC.md milestone table records what each delivered. For new work, keep SPEC.md authoritative — update it in the same change rather than letting code and spec diverge.
Resolved design decisions
Previously open; settled during implementation. See SPEC.md → "Design decisions (resolved)" for the full log.
- Claude's
/renameJSONL format was reverse-engineered from a real transcript: acustom-titleevent. The index falls back to adisplay_nameoverride only if that format ever proves volatile. - Preview pane content is settled: name, project, folder, branch, age, created date, message count, context size, session id, notes, first prompt, and transcript path (the never-populated
summaryblock was dropped).
Two items remain deliberately deferred (see SPEC.md): in-place /compact and empty-folder pruning.
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.

