refactor(config): platform-aware, single-source user config locations#203
Merged
Conversation
Resolve the user-level config trio under both fs::path_home() and fs::path_home_r() so Windows honors R's home directory for agents and skills, matching the existing btw.md config-file search. macOS/Linux entries collapse via unique(), leaving behavior unchanged.
Warn once per session when the btw <= 1.2.0 skills location
(R_user_dir("btw", "config")/skills) actually contains skills, pointing
users to ~/.btw/skills. The legacy directory is still read; read support
will be removed in btw 1.5.0.
Add a user-visible ?btw-config reference documenting all project and user configuration locations, and update btw_client(), use_btw_md(), and the skill functions to describe their own locations completely and link to it. Clarify the discover_agent_md_files() priority comment.
Address review finding on the docs/behavior mismatch for user-level
btw.md. use_btw_md("user") now creates ~/.btw/btw.md and offers to
migrate an existing ~/btw.md rather than creating a shadowed file. A
loose ~/btw.md still takes precedence when loading; path_find_user()
warns once per session when more than one user-level btw.md is found.
Documentation across btw-config, btw_client(), and use_btw_md() updated
to describe this precedence accurately.
Convert the message-content assertions for use_btw_md("user") and the
multiple-config warning to snapshot tests. Add path_home_display() to
render home-relative paths without requiring them to exist, so the
migration advisory and warning show ~/... paths (deterministic in
snapshots and tidier for users).
use_btw_md("user") and edit_btw_md("user") only reasoned about ~/btw.md
and ~/.btw/btw.md, while the loader searches the full ordered set. This
could create a shadowing ~/.btw/btw.md over an active config in
~/.config/btw/, R_user_dir, or (Windows) R's home root, and the
"already exists" hint pointed at edit_btw_md("user") which actually
opened the higher-priority ~/btw.md.
- Add user_config_paths() as the single ordered source; path_find_user()
builds on it. Add existing_user_btw_md() returning all existing
user-level btw.md in priority order.
- use_btw_md("user") creates ~/.btw/btw.md only when no user config
exists anywhere; otherwise offers to migrate/consolidate the active
file. maybe_migrate_user_btw_md() no longer clobbers an existing
recommended file.
- edit_btw_md("user") presents a menu of existing configs, marking the
one read by btw_client()/btw_app() and nudging consolidation.
- Add local_user_home() test isolation plus cases for non-loose
migration, no-clobber, and the edit menu; update snapshots and docs.
path_home_display()/path_display() abbreviated ~ against fs::path_home()
(user profile), but R expands ~ via fs::path_home_r(). On Windows those
differ, so a recommended path printed as ~/.btw/btw.md pointed at a
different file than the one created/migrated, risking another shadowed
config. Same defect in the legacy-skills warning.
- Add btw_user_dir_preferred() = btw_user_dirs()[[1]] as the single
source for the recommended user dir; use it in use_btw_md("user"),
edit_btw_md("user"), warn_multiple_user_config(), and the legacy
skills warning instead of hardcoding fs::path_home(".btw").
- Abbreviate ~ against fs::path_home_r() so profile-only paths render
natively rather than as a misleading ~. macOS/Linux unchanged.
- Add unit tests for btw_user_dir_preferred() and path_home_display().
Address test-review findings: - Route identical-home-root tests through local_user_home() and pin R_USER_DATA_DIR in distinct-root tests so tools::R_user_dir() no longer resolves to the developer's real user dir (removes contamination risk and the implementation-mirror in the btw_user_dirs() expectations). - Add branch coverage for maybe_migrate_user_btw_md() decline and resolve_user_btw_md_for_edit() cancelled-selection paths. - Consolidate the legacy-skill-dir warning test into a single captured warning asserted for both message parts.
gadenbuie
marked this pull request as ready for review
July 9, 2026 23:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
btw_user_dirs()is now the single, platform-aware source of truth for where btw looks for user-level agents, skills, and config. On Windows it now also searches R's home directory (fs::path_home_r(), typicallyDocuments) in addition to the user profile, since the two can differ; macOS/Linux behavior is unchanged.User-facing changes:
?btw-confighelp page documents every user and project configuration location in one place.btw_client(),use_btw_md()/edit_btw_md(), and the skill-install functions link to it and describe their own relevant locations.use_btw_md("user")now creates~/.btw/btw.md(the recommended location) instead of the old~/btw.md. If a~/btw.mdalready exists, it offers to migrate it instead of creating a second, shadowed file.~/btw.mdstill takes precedence as a config file when present — this asymmetry (loose file wins for config,~/.btw/trio wins for skills/agents) is intentional and documented in?btw-config.btw.mdexists, btw picks the highest-priority one and warns once per session, naming the file in use.edit_btw_md("user")shows a menu of all existing user configs when more than one exists, marking the active one.tools::R_user_dir("btw", "config")/skills) is deprecated: btw still reads it but warns once per session when it actually contains skills. Read support will be removed in btw 1.5.0.For developers extending this code:
btw_user_dirs()(R/utils.R) is the ordered list of user-level directories (agents, skills); iterate it and take the first match.user_config_paths(filename)(R/utils.R) is the ordered list of user-level file candidates for a given filename, including the~/btw.md-style home-root convenience entries that directories don't have.path_find_user()builds on this — use it rather than re-deriving the search set.btw_user_dir_preferred()(R/utils.R) =btw_user_dirs()[[1]], the single source for "the recommended location to create a new file/dir in." Use this instead of hardcoding~/.btw.path_home_display()/path_display()(R/utils.R) abbreviate paths againstfs::path_home_r()(what R's~actually expands to), so user-facing messages showing recommended/active paths stay accurate on Windows.existing_user_btw_md()andmaybe_migrate_user_btw_md()(R/edit_btw_md.R) are the building blocks for "does a user config already exist, and should we offer to migrate it" — reuse these rather than re-implementing the shadowing checks if you add another user-scoped file type.