From filetree
Shared rules for /filetree:init and /filetree:update commands that generate or sync FILETREE.md. Controls summary style, language resolution, and UNCHANGED bias for efficient updates.
How this skill is triggered — by the user, by Claude, or both
Slash command
/filetree:filetreeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Cross-cutting rules used by `/filetree:init` and `/filetree:update`. The commands themselves contain step-by-step flows; this file holds rules that apply across modes so they're maintained in one place.
Cross-cutting rules used by /filetree:init and /filetree:update. The commands themselves contain step-by-step flows; this file holds rules that apply across modes so they're maintained in one place.
/filetree:lint is read-only script invocation and does not need these rules.
One line, max 25 words, describes what the file is FOR (its role / purpose). Not what it implements internally.
Present tense. No marketing words. For the language to write summaries in, see "Summary language" below — never pick per-file.
One run, ONE language. Every summary in the manifest — and the command's own narration — uses it. Without a single anchor, parallel sub-agents each guess and the manifest ends up mixing Chinese and English.
The command resolves the canonical language ONCE, up front, by this priority:
config.language from the todo output (set when .filetree.json pins language). When present it is authoritative — skip the rest of the chain.CLAUDE.md / AGENTS.md (the agent contract — most authoritative).README (any localized variant)./filetree:update only) the dominant language of existing manifest entries.Then it passes that one language verbatim into EVERY sub-agent prompt ("Write all summaries in "). Sub-agents never re-detect; they run in parallel and would diverge if left to choose.
Scope. This entire section applies to
/filetree:updateonly. During/filetree:initthe manifest starts empty, so there is no old summary to keep —UNCHANGEDhas nothing to refresh andapplywill drop it. In init, every file gets a real summary. Do not apply this bias to init sub-tasks.
Why this matters. Hash changes trigger the LLM, but most code changes (typos, refactors, comments, small additions) don't change a file's purpose. Outputting "UNCHANGED" lets cmd_apply refresh just the hash and keep the existing summary — the manifest itself carries the memory of "I already reviewed this version". In a healthy update run, 80%+ of changed items should resolve to UNCHANGED. Writing a fresh 25-word summary when the old one still fits wastes ~100x more tokens than a 4-byte "UNCHANGED" reply.
Decision rule. You have: old summary, old hash, new hash, and the file's new content (prefer reading the git diff over the full file — diff is far denser per token and is all you need for purpose-level judgement). If the diff comes back EMPTY (the change was already committed, so working tree == HEAD), fall back to reading the file — the hash moved, so judging purpose from a blank diff would falsely yield UNCHANGED.
Output "UNCHANGED" if the old summary still describes the file's PURPOSE. Refactors, renames, bug fixes, test additions, formatting, comment changes, small additions — these almost always leave the purpose intact.
Output a new summary string only if:
When in doubt (and the language already matches), output UNCHANGED.
The pressure to "be thorough" pushes toward rewriting. Each excuse below is a trap; the right answer is UNCHANGED.
| Excuse | Reality |
|---|---|
| "The diff is large, so I should rewrite" | Diff size ≠ purpose change. A 500-line refactor with the same role is UNCHANGED. |
| "Let me polish the old summary while I'm here" | Polishing burns ~100x the tokens of UNCHANGED and isn't an exception. Only purpose change or wrong language qualifies. |
| "It's slightly more accurate now" | "Slightly better wording" is not "purpose changed". UNCHANGED. |
| "I'm not sure the purpose changed" | Not sure = it didn't. UNCHANGED. |
| "New function added, must re-describe" | A helper added to the same role doesn't expand purpose. UNCHANGED. |
old_summary in new wordsgit diff already answers the purpose questionAll of these mean: output "UNCHANGED".
Some added / changed items carry a symlink_target field. For those: do not Read the file — a Read follows the link to the target's content (wasteful, and fails on a broken link). Write exactly symlink → <target> using the supplied symlink_target; do not infer a role you can't see. The script already hashes symlinks correctly from the link string.
todo --split)Always run todo --split (the script chunks the LLM work and writes it to files, so you never count, truncate, or hand-split). Output:
{ "stats": {...}, "removed": [...], "renamed": [...],
"manifest_exists": true,
"config": {"manifest_path": "FILETREE.md", "language": null},
"split_dir": "/tmp/filetree_XXXX",
"batches": [{"file": ".../batch_00.json", "count": 25}, ...] }
The config block reflects .filetree.json (the script is the only config parser — never re-read the file yourself). manifest_path is where the manifest lives (may be renamed / relocated); language pins the summary language (see "Summary language" priority 0). exclude / include filtering is already applied inside the script, so the work plan only lists files that belong in the manifest. manifest_exists is whether the manifest file is already on disk — /filetree:update uses it to detect a not-yet-initialized repo (a present-but-empty manifest also reads total_in_manifest: 0, so the boolean is the reliable signal).
Each batch_NN.json is a JSON array of todo items (added + changed). Drive it purely off batches:
echo '{"updates": []}' | python3 .../filetree.py apply
<split_dir>/part_00.json.claude-haiku-4-5 sub-agent per batch (good enough, ~10x cheaper). Each sub-agent: Read SKILL.md, Read its assigned batch_NN.json, then write <split_dir>/part_NN.json. Sub-agents run in parallel and never see each other's batch.Each part_NN.json carries ONLY summaries — hash is computed from disk, removed/renamed are recomputed from repo state, so neither belongs here:
{"updates": [{"path": "...", "summary": "..." | "UNCHANGED"}]}
Apply all parts in one call (the shell expands the glob, the script merges):
python3 .../filetree.py apply <split_dir>/part_*.json
apply, never a hand-rolled diffBefore claiming the manifest is synced, run this gate on apply's return:
missing_from_manifest — any indexable file still without an entry (a dropped sub-agent output, a forgotten file). This is the completion gate.skipped_unchanged_new (a wrong UNCHANGED), skipped_missing_path (a hallucinated path).apply (it merges). Loop until both clear.skipped_excluded — real files the config keeps out, nothing to fix. Do NOT gate on applied == received: a legitimate skipped_excluded makes applied < received hold forever, which would loop here.apply's return.Never hand-roll a coverage diff (concatenating batch lists, comparing counts): it is redundant and error-prone. The script's keys are the only evidence.
npx claudepluginhub nekocode/filetree-skill --plugin filetreeAnalyzes local modifications in Memory Bank files: computes MD5 hashes, detects new/ modified files, classifies changes like new sections or added lines, and provides merge strategies.
Builds and maintains the deterministic project manifest under .smith/index/ for structured context retrieval by other Smith skills. Supports full rebuild, incremental updates, hash-only staleness checks, and per-file LLM descriptions.
Synchronizes CLAUDE.md navigation indexes and README.md architecture docs across repositories or directories. Use for audits after code changes or triggers like 'sync docs', 'update CLAUDE.md'.