x4-modlist-review
Review and triage the X4 mod registry (Phase-A worklist) — scan the ACTUALLY INSTALLED extension folders (the primary source of truth), cross-check against the old profile content.xml (diff/backstop o
SKILL.md.claude/skills/x4-modlist-review/SKILL.md
GitHub<!-- GENERATED by tools/x4validate/scripts/gen-agent-trees.py from agent/. Do not edit: regenerate. -->
Triage the X4 modlist via the x4modlist CLI. API-FIRST — never scrape Nexus. Registry: $X4_MODS/_registry/modlist.yaml (or $X4_REGISTRY if set — the tool resolves it; x4validate --paths shows where); human dashboard: WORKLIST.md next to it.
★ SOURCE OF TRUTH: the physically INSTALLED extension folders are PRIMARY — game-root extensions\, profile extensions\ (if present), Steam Workshop content\392160\ (if present). That's what the game actually loads. For the INVENTORY, the profile's content.xml enabled-list is a SECONDARY cross-check only ("did I forget to re-acquire something from my old modlist?") — it keeps entries for mods long gone from disk. For what is ACTIVE it does decide: an installed mod's profile entry overrides its manifest's enabled, and a mod with no entry falls back to its manifest (enabled unless the manifest says enabled="0"). A mod tracked historically but not found on disk shows up in a separate "OLD MODLIST — NOT CURRENTLY INSTALLED" dashboard section, not in the active lanes.
Run commands via uv from the tool dir:
cd $CLAUDE_PROJECT_DIR/tools/x4validate && uv run --python 3.13 x4modlist <cmd>
Needs X4_NEXUS_KEY (user env). If a command errors "X4_NEXUS_KEY not set", the user must set it (endpoints, rate budget and how to get a key: x4validate/_nexus.py).
Establish the current situation
Before proposing changes, read ## My preferences in KNOWLEDGEBASE.local.md
and reconcile it with the current request and session context: is this a kept save,
a new game or a disposable test, and are updates, additions, removals or ordering
changes authorized? Assume neither an ongoing playthrough nor a fresh game.
Ask only about material facts that remain unknown or contradictory; a missing
local file alone does not justify repeating an answered question. Continue
independent read-only work while waiting. Apply standing user rulings before
choosing work, and record new lasting rulings, dated, in that user-owned section.
Keep it concise; if session context is truncated, read the full section directly.
These choices do not waive verified backups, write guards or scoped authorization.
Workflow
Before adopting a mod, x4modlist search "name" [--limit 15] [--json] returns candidate
Nexus identities, versions, update dates and status without changing the registry. It needs
X4_NEXUS_KEY (environment or x4-paths.env) and says so when the key is missing or refused.
It labels complete, zero, partial and unavailable searches; a capped or damaged page is never
a complete negative (the malformed/duplicate count is printed). Each candidate is marked
against the ACTIVE registry mods: have = a pinned/exact Nexus id, have? = an unconfirmed
guess, new = no active mod carries that id. The output also states how many active mods lack
a Nexus id (so new cannot rule those out); with no usable registry it says marking is
unavailable instead of marking everything new. Every result ends with its coverage: names
only (never descriptions), hidden/removed mods and Steam Workshop-only mods are absent, and an
empty result means none within that coverage, not that none exist. Search says nothing about
game compatibility.
- Ingest —
x4modlist ingestscans the installed folders (reading each mod's OWNcontent.xmlfor its realid/name/version/author— folder names can differ from the manifestid, e.g. a folder named in CamelCase whose manifestiduses underscores, spaces and an extra suffix) and merges that as PRIMARY; the old profile content.xml is a secondary backfill pass so nothing tracked historically is silently dropped.--installed-onlyskips the content.xml pass;--dirs a,b,coverrides the scanned directories. - Refresh —
x4modlist refreshpulls upstream version/status and auto-resolves identities for installed mods. Identity resolution prefers each mod's REAL manifest name (far more reliable than guessing from the folder/id) over a humanized-id guess, with fallbacks for common author-prefix ("<author>: X" → "X") and suffix-qualifier ("X - Some Edition" / "X <overhaul>" → "X") naming patterns.--forcebypasses the once-per-day TTL;--ids a,b,ctargets specific mods (works even for NOT-installed old-list mods, to check upstream status before deciding to re-acquire). "Has an update" means one thing, with a grace window: upstream's newest MAIN file was uploaded more thanUPDATE_GRACE_DAYS(3) days after the installed copy's manifestdate— both dates are printed on everyUPDATEline and in the dashboard's ⬆ UPDATE AVAILABLE table. A mod pinned to a FILE (resolve --file) is judged against that file's newest successor on the page (itsfile_updateschain), or the pinned file itself when it has none. Version strings are not compared (Nexus uses several shapes; manifests carry integers). The verdict isavailable/same-release?(upload within the 3-day window — likely the same release, not a new one, printed with both dates and counted in the tally, but excluded from the UPDATE AVAILABLE table) /none/unknown(a date is missing or unparseable — never read that as "no update") /unconfirmed(the identity is a guess, so the upstream may be another mod). A verdict the current run did not re-check (once-per-day TTL) is labelled carried over with its check date; a row that now ends in error/untriaged/off-nexus carries no verdict at all. ⚠ UNMEASURED: authors commonly date the manifest before finishing the upload, which is why the grace window exists — but its own false-positive rate (asame-release?that was actually a fast follow-up release) and false-negative rate (a real update landing inside the window) are both unmeasured until the next online refresh checks some by hand. Compare the printed upstream file/version before re-downloading either way. A refresh that hits a missing/invalid key, a spent rate limit or a dead network stops (exit 2), keeps every row it already fetched, and leaves the rest untouched. - Present — read
WORKLIST.md; summarize the lanes (✅ ready / ⏸ churning / ⚠ predates-9.0 / 🔧 custom-local / ❌ drop), the Updates tally and ⬆ UPDATE AVAILABLE rows (with both dates), the NEEDS SPOT-CHECK count, and the OLD MODLIST — NOT CURRENTLY INSTALLED count (mods to potentially re-acquire). - Spot-check loop —
x4modlist needs-reviewlists entries needing a human call. For each, show the auto-matched candidate(s) and have the user decide:- confirm/correct identity →
x4modlist resolve <id> <nexus_id> - junk/personal/cheat mod →
x4modlist ignore <id> --reason "..." - a mod they locally customized →
x4modlist mark <id> --custom --notes "..."Never guess keep/drop or fabricate a match — surface candidates, the user decides. Search can return a wrong/deprecated/adoption-patch fork as the top hit (searching a mod's full name can match a niche adoption/compat fork instead of the real, popular mod) — always show the candidate list, don't just trust rank #1.
- confirm/correct identity →
- Deep 9.0-readiness (opt-in only) — the API gives version/date/status but NOT "9.0-compatible" (that gap is real; changelogs are sparse). For churning / predates-9.0 mods the user wants to keep, dispatch the
mod-researchagent (API-first) for changelog/community signal. Only on request.
The 🔧 CUSTOM-LOCAL FORK lane
If a mod's Nexus status is removed/hidden AND it's marked custom_edited, it is classified custom-local — NOT drop. An author temporarily hiding their page mid-update (or a mod you've locally forked/ported yourself, e.g. in dev\) doesn't mean abandon it; "drop" would be actively wrong guidance there. This only applies once the user has marked the mod custom-edited.
Honest framing
This produces the auto-resolved worklist + the spot-check queue. The keep/drop/custom decisions and in-game testing are the user's — it makes Phase A tractable, not instant. Strategy: work the ready + churning lanes first (the live mods); let predates-9.0 and unresolved bake, and re-refresh later as authors ship 9.0 updates. Periodically re-ingest to catch newly-installed/removed folders.