Skip to content

fix: avoid creating redundant snippet/ dir when snippets/ already exists + path tracking refactor#34

Open
bew wants to merge 1 commit into
JosXa:mainfrom
bew:fix-config-dir-selection
Open

fix: avoid creating redundant snippet/ dir when snippets/ already exists + path tracking refactor#34
bew wants to merge 1 commit into
JosXa:mainfrom
bew:fix-config-dir-selection

Conversation

@bew

@bew bew commented May 18, 2026

Copy link
Copy Markdown

Hello! I'm trying your plugin and really liking it!

⚠️ However, I've found a bug:
If you already have a snippets/ directory (with a config inside or not), the plugin creates a brand new snippet/ directory and drops a fresh config there on every startup, completely ignoring my existing snippets/ directory..

This PR fixes this! 🚀
I ended up doing a small refactor around path handling, which felt necessary to make the fix clean.

The old PATHS constant didn't distinguish between the preferred dir, the alt dir, and the currently active one, I think that ambiguity was the root cause of the bug. 🤔
👉 I replaced it with a GLOBAL_PATHS object typed as SnippetPaths, which makes all three explicit.

The selection logic lives in a small pure function resolveSnippetDir(preferred, alt) that's easy to test in isolation (which I did).
The new GLOBAL_PATHS is populated once on startup with getGlobalPaths which works the same way as getProjectPaths, with the new active dir selection.

The same fix applies to project-scoped paths (.opencode/snippet/ vs .opencode/snippets/), which had the same issue.

Existing tests have been updated, and all 341 tests pass!


One thing I noticed while digging around: writeState writes runtime state into the config directory.
This feels like it goes against XDG conventions, and runtime state probably belongs in $XDG_STATE_HOME (or $XDG_RUNTIME_DIR for truly ephemeral data) instead of $XDG_CONFIG_HOME.
👉 Happy to make a separate PR if you think this is a good idea!

When the alt `snippets/` directory already contained a config, the old code
always wrote a new `snippet/` dir and config there — because paths were
hardcoded to the preferred dir regardless of what existed on disk.

Fix by computing the active snippets dir once at startup via
`resolveSnippetDir`: uses alt only when it exists and preferred does
not, falls back to preferred in all other cases.

The same resolution applies to project-scoped snippets — both `snippet/`
and `snippets/` are now scanned, with the active dir chosen by the same
logic.

The fix is supported by a broader path-tracking refactor:
- Add `SnippetPaths` interface with unified fields for
  global/project-scoped paths.
- Isolate the dir selection logic in easily testable pure function:
  `resolveSnippetDir(preferred, alt)`
- Replace `PATHS` export with `GLOBAL_PATHS` (computed once at startup)
- Update all consumers (`config.ts`, `loader.ts`, `logger.ts`,
  `pending-drafts.ts`, `reload-signal.ts`, `commands.ts`)
- Refactor tests to override `GLOBAL_PATHS` fields directly;
  add `withGlobalDir` helper in integration tests
- Add `src/constants.test.ts` covering all four `resolveSnippetDir` cases
@bew

bew commented Jun 6, 2026

Copy link
Copy Markdown
Author

Hello @JosXa , friendly ping 🙃

Would it be possible to get a review and potentially merge this PR ?
Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant