Skip to content

fix: include cache bin directory in which() lookups#18320

Merged
thdxr merged 1 commit intodevfrom
refactor/which-cache-bin
Mar 20, 2026
Merged

fix: include cache bin directory in which() lookups#18320
thdxr merged 1 commit intodevfrom
refactor/which-cache-bin

Conversation

@thdxr
Copy link
Copy Markdown
Member

@thdxr thdxr commented Mar 20, 2026

Summary

  • Move Global.Path.bin from data directory to cache directory
  • Append Global.Path.bin to PATH in which() so tools installed via npm in the cache are discoverable

Move Global.Path.bin from the data directory to the cache directory,
and append it to PATH in which() so tools installed via npm are
discoverable without being on the system PATH.
@thdxr thdxr merged commit 6fcc970 into dev Mar 20, 2026
10 checks passed
@thdxr thdxr deleted the refactor/which-cache-bin branch March 20, 2026 01:21
thdxr added a commit that referenced this pull request Mar 20, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 20, 2026

Greptile Summary

This PR relocates Global.Path.bin from the XDG data directory to the XDG cache directory and ensures tools installed there are discoverable by appending the path to PATH inside which(). The motivation is correct — npm-installed tools should be found by which() — but moving bin into the cache directory introduces a silent regression where all installed tools are wiped whenever CACHE_VERSION changes.

Key changes:

  • global/index.ts: bin path changed from path.join(data, "bin") to path.join(cache, "bin")
  • which.ts: Builds a composite PATH string that appends Global.Path.bin to the inherited environment PATH before resolving commands

Issues found:

  • The cache-invalidation block in global/index.ts deletes all contents of Global.Path.cache on version bumps. Since bin is now a subdirectory of cache, installed tools will be silently deleted on any future CACHE_VERSION change, breaking tool discovery until they are reinstalled — and there is no visible reinstallation logic triggered after a wipe.
  • Minor style note: the full variable in which.ts is used only once and should be inlined per the project style guide.

Confidence Score: 3/5

  • Mergeable in concept, but the cache-invalidation interaction will silently wipe installed tools on any future CACHE_VERSION bump.
  • The which() fix is correct and the intent of the bin relocation is sound, but placing bin inside the cache directory without excluding it from cache wipes creates a latent regression that will surface on the next version bump. This needs to be addressed before merging.
  • packages/opencode/src/global/index.ts — cache-invalidation logic needs to skip or handle the bin subdirectory.

Important Files Changed

Filename Overview
packages/opencode/src/global/index.ts Moves bin from data/bin to cache/bin. The existing cache-invalidation block will wipe the bin directory (and any installed tools inside it) whenever CACHE_VERSION changes, which is a silent regression for tool discoverability.
packages/opencode/src/util/which.ts Correctly appends Global.Path.bin to the resolved PATH so npm-installed tools in the cache bin are discoverable. Minor style nit: full is a single-use variable that should be inlined per the project guide.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant which
    participant whichPkg
    participant Global

    Caller->>which: which(cmd, env?)
    which->>Global: Global.Path.bin
    Global-->>which: ~/.cache/opencode/bin
    which->>which: base = env.PATH ?? process.env.PATH ?? ""
    which->>which: full = base + ":" + ~/.cache/opencode/bin
    which->>whichPkg: sync(cmd, { path: full })
    whichPkg-->>which: resolved path | null
    which-->>Caller: string | null
Loading

Comments Outside Diff (1)

  1. packages/opencode/src/global/index.ts, line 41-53 (link)

    P1 Cache wipe deletes installed bin tools

    bin is now located inside Global.Path.cache (cache/bin). The cache-invalidation block (lines 41–53) deletes all contents of the cache directory when CACHE_VERSION changes. This means every tool previously installed via npm into cache/bin will be silently wiped on any future cache-version bump — including any currently-pending upgrade from "20""21".

    Any code that installs tools into Global.Path.bin would need to re-install them after a cache wipe, but that reinstallation logic is not visible here. Unless every call-site that writes into bin handles the case where bin is empty, tool discovery via which() will silently fail after a cache reset.

    Consider either:

    1. Excluding the bin subdirectory from the cache wipe, or
    2. Moving bin to a path that is not under Global.Path.cache (e.g. back under data, or under state) so it survives cache invalidation.

Last reviewed commit: "fix: include cache b..."

Comment on lines +6 to +7
const base = env?.PATH ?? env?.Path ?? process.env.PATH ?? process.env.Path ?? ""
const full = base ? base + path.delimiter + Global.Path.bin : Global.Path.bin
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Inline single-use variable per style guide

full is used exactly once (on line 10), so per the project style guide it should be inlined. base is used twice and should stay. Consider inlining full directly into the path: property, potentially simplifying via a helper or by restructuring the ternary inline.

Suggested change
const base = env?.PATH ?? env?.Path ?? process.env.PATH ?? process.env.Path ?? ""
const full = base ? base + path.delimiter + Global.Path.bin : Global.Path.bin
const base = env?.PATH ?? env?.Path ?? process.env.PATH ?? process.env.Path ?? ""
const result = whichPkg.sync(cmd, {
nothrow: true,
path: base ? base + path.delimiter + Global.Path.bin : Global.Path.bin,

Context Used: AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant