Implement r2modmanPlus-style BepInEx install rules with marker-based cache validation#11
Draft
Claude wants to merge 4 commits intofeat/new-download-extraction-handlingfrom
Draft
Conversation
This commit implements the complete specification for fixing BepInEx mod
installation to behave like r2modmanPlus, addressing the "sometimes missing
files" issue and adding support for all BepInEx routes.
Changes made:
A) Extraction cache correctness (downloader.ts)
- Added marker-based cache hit validation using _extracted.ok file
- Cache is now valid ONLY when both extractPath and marker exist
- Cleans up stale/partial extractions before re-extracting
- Writes marker after successful extraction
- Removes marker on extraction failure
- Added detailed logging for cache hit/miss scenarios
B) Effective root resolution (mod-installer.ts)
- Added resolveEffectiveRoot() function to handle nested folder zips
- Checks for BepInEx at top level first
- Falls back to searching immediate children for BepInEx folders
- Supports common Thunderstore layout: SomeFolder/BepInEx/...
- Uses deterministic sorting for consistent behavior
C) Generic BepInEx route handling (mod-installer.ts)
- Now installs all BepInEx routes when present:
* plugins/ (namespaced under modId)
* patchers/ (namespaced under modId)
* core/ (namespaced under modId)
* monomod/ (namespaced under modId)
* config/ (merged, not namespaced)
- Added idempotency: removes destination folders before copying
- Config files are merged into shared config directory
- Fallback behavior maintained for mods without BepInEx structure
- Comprehensive logging of which routes were found and installed
D) Uninstall logic updated (mod-installer.ts)
- Now removes all namespaced route folders:
* BepInEx/plugins/<modId>
* BepInEx/patchers/<modId>
* BepInEx/core/<modId>
* BepInEx/monomod/<modId>
- Config files remain untouched (shared between mods)
- Logs removed paths and file counts
E) Deterministic logging throughout
- effectiveRoot resolution decision and reason
- Cache hit/miss reasons (extractPath exists, marker exists, ignoreCache)
- Which source routes existed in the mod
- Which destination routes were written
- File counts for each operation
- Uninstall operations with paths and counts
This fixes the primary issue of "sometimes missing files" caused by partial
extraction cache and ensures all BepInEx route types are installed correctly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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.
Fixes "sometimes missing files" issue caused by stale extraction cache and adds support for all BepInEx route types (patchers, core, monomod) with proper nested folder handling.
Changes
Cache correctness (downloader.ts)
_extracted.okgates cache hits—both extractPath and marker must existNested folder support (mod-installer.ts)
resolveEffectiveRoot()handlesSomeFolder/BepInEx/...zip layouts common on ThunderstoreBepInEx routes (mod-installer.ts)
plugins/,patchers/,core/,monomod/(namespaced),config/(merged)Uninstall (mod-installer.ts)
BepInEx/{plugins,patchers,core,monomod}/<modId>Example
Before: A mod with
BepInEx/patchers/would only installplugins/, silently dropping patchers. If extraction was interrupted, partial cache would be reused.After: All routes install correctly. Interrupted extractions trigger clean re-extraction.
Original prompt
Goal
Non-goals (for this first pass)
Specifications
A) Extraction cache correctness (marker-based cache hit)
Applies to: electron/downloads/downloader.ts
Marker file
Cache hit rule (new)
If either is missing, treat cache as invalid and re-extract.
Re-extract rule (new)
Failure handling
Rationale
B) Effective root resolution (nested top-level folder zips)
Applies to: electron/profiles/mod-installer.ts
Definition
Algorithm
Case handling
Rationale
C) Generic BepInEx route handling (r2modmanPlus-style routes)
Applies to: electron/profiles/mod-installer.ts
When effectiveRoot/BepInEx exists
Install these routes if present in the extracted content:
Idempotency / reinstall semantics
When effectiveRoot/BepInEx does NOT exist
Counting / return values
D) Uninstall logic must match install destinations
Applies to: electron/profiles/mod-installer.ts
Uninstall removes (new)
Uninstall does NOT remove
Implementation Plan (step-by-step)
Files: electron/downloads/downloader.ts