Skill161 repo starsupdated 7d ago
filetree
The filetree skill defines cross-cutting rules for file tree initialization and updates, including standards for writing file summaries, selecting a canonical language across the manifest, and preserving unchanged summaries during updates to avoid redundant token use.
Install in Claude Code
Copygit clone --depth 1 https://github.com/nekocode/filetree-skill /tmp/filetree && cp -r /tmp/filetree/skills/filetree ~/.claude/skills/filetreeThen start a new Claude Code session; the skill loads automatically.
Definition
SKILL.md
# Filetree Skill — Shared Rules
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.
---
## Summary style
One line, max 25 words, describes what the file is FOR (its role / purpose). Not what it implements internally.
- Good: "JWT auth middleware; parses token from request header and injects user_id into context"
- Bad: "Defines AuthMiddleware class with __init__ and __call__ methods"
- Bad: "Handles auth" (too vague)
Present tense. No marketing words. For the language to write summaries in, see "Summary language" below — never pick per-file.
---
## Summary language
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:
0. `config.language` from the `todo` output (set when `.filetree.json` pins `language`). When present it is authoritative — skip the rest of the chain.
1. Else the dominant natural language of `CLAUDE.md` / `AGENTS.md` (the agent contract — most authoritative).
2. Else `README` (any localized variant).
3. Else (`/filetree:update` only) the dominant language of existing manifest entries.
4. Else English.
Then it passes that one language verbatim into EVERY sub-agent prompt ("Write all summaries in <language>"). Sub-agents never re-detect; they run in parallel and would diverge if left to choose.
---
## UNCHANGED bias (for /filetree:update ONLY)
> **Scope.** This entire section applies to `/filetree:update` only. During `/filetree:init` the manifest starts empty, so there is no old summary to keep — `UNCHANGED` has nothing to refresh and `apply` will 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:
- A major new feature has been added that meaningfully expands purpose
- A previously central concern has been removed
- The file has been substantially rewritten for a different goal
- **The old summary is in the wrong language** (not the run's canonical language — see "Summary language"). Rewrite it in the target language even if the purpose is unchanged; this is how a legacy mixed-language manifest converges — gradually, as each file's hash changes and re-enters the work plan. Language mismatch ALWAYS overrides the UNCHANGED bias.
When in doubt (and the language already matches), output UNCHANGED.
### Rationalizations — every one resolves to 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. |
### Red flags — STOP, you're about to waste tokens
- About to write a summary that says the same thing as `old_summary` in new words
- Justifying a rewrite by how much the code changed rather than whether the role changed
- "Improving" or "tidying" a summary whose language already matches
- Reading the full file when the `git diff` already answers the purpose question
**All of these mean: output `"UNCHANGED"`.**
---
## Symlinks
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.
---
## Processing the work plan (`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:
```json
{ "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 configMore from this repository
initSlash Command
Generate FILETREE.md from scratch. Confirms overwrite if it already exists.
lintSlash Command
Report drift between FILETREE.md and current repo, read-only. Prompts to run /filetree:update on drift.
updateSlash Command
Sync FILETREE.md with current repository state — handles added / changed / removed / renamed.