Previously cheat only checked the current working directory for a .cheat subdirectory. Now it walks upward through ancestor directories, stopping at the first .cheat directory found. This mirrors how git discovers .git directories, so users can place .cheat at their project root and have it work from any subdirectory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2.7 KiB
ADR-004: Recursive .cheat Directory Search
Date: 2026-02-15
Status
Accepted
Context
Previously, cheat only checked the current working directory for a .cheat
subdirectory to use as a directory-scoped cheatpath. If a user was in
~/projects/myapp/src/handlers/ but the .cheat directory lived at
~/projects/myapp/.cheat, it would not be found. Users requested (#602) that
cheat walk up the directory hierarchy to find the nearest .cheat
directory, mirroring the discovery pattern used by git for .git
directories.
Decision
Walk upward from the current working directory to the filesystem root, and
stop at the first .cheat directory found. Only directories are matched (a
file named .cheat is ignored).
Stop at first .cheat found
Rather than collecting multiple .cheat directories from ancestor directories:
- Matches
.gitdiscovery semantics, which users already understand - Fits the existing single-cheatpath-named-
"cwd"code without structural changes - Avoids precedence and naming complexity when multiple
.cheatdirectories exist in the ancestor chain cheatalready supports multiple cheatpaths viaconf.ymlfor users who want that; directory-scoped.cheatserves the project-context use case
Walk to filesystem root (not $HOME)
Rather than stopping the search at $HOME:
- Simpler implementation with no platform-specific home-directory detection
- Supports sysadmins working in
/etc,/srv,/var, or other paths outside$HOME - The boundary only matters on the failure path (no
.cheatfound anywhere), where the cost is a few extrastatcalls - Security is not a concern since cheatsheets are display-only text, not executable code
Consequences
Positive
- Users can place
.cheatat their project root and it works from any subdirectory, matching their mental model - No configuration changes needed; existing
.cheatdirectories continue to work identically - Minimal code change (one small helper function)
Negative
- A
.cheatdirectory in an unexpected ancestor could be picked up unintentionally, though this is unlikely in practice and matches how.gitworks
Neutral
- The cheatpath name remains
"cwd"regardless of which ancestor the.cheatwas found in
Alternatives Considered
1. Stop at $HOME
Rejected: Adds platform-specific complexity for minimal benefit. The only
downside of walking to root is a few extra stat calls on the failure path.
2. Collect multiple .cheat directories
Rejected: Introduces precedence and naming complexity. Users who want
multiple cheatpaths can configure them in conf.yml.
References
- GitHub issue: #602
- Implementation:
findLocalCheatpath()ininternal/config/config.go