- Remove unused parameters, dead files, and inaccurate doc.go files - Extract shared helpers, eliminate duplication - Rename cheatpath.Cheatpath to cheatpath.Path - Optimize filesystem walks (WalkDir, skip .git) - Move sheet name validation to sheet.Validate - Move integration tests to test/integration/ - Consolidate internal/mock into mocks/ - Move fuzz.sh to test/ - Inline loadSheets helper into command callers - Extract config.New into its own file - Fix stale references in HACKING.md and CLAUDE.md - Restore plan9 build target - Remove redundant and low-value tests - Clean up project documentation 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