add CLAUDE.md for Claude Code context
Covers: common commands, package vs CLI architecture, data flow, group naming convention, server index structure, junction/symlink rules (critical: never shutil.rmtree), check_names two-pass classification, Python 3.9 from __future__ requirement, and test suite structure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
105
CLAUDE.md
Normal file
105
CLAUDE.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
# Run all tests (no network required)
|
||||
python test_suite.py
|
||||
|
||||
# Check Python version and dependencies
|
||||
python check_deps.py
|
||||
|
||||
# Full pipeline (parse → compare → fetch → link)
|
||||
python run.py
|
||||
|
||||
# Parse + compare only (no download, no linking)
|
||||
python run.py --skip-fetch --skip-link
|
||||
|
||||
# Diagnose mod folder name / steam_id issues
|
||||
python check_names.py
|
||||
python check_names.py --fix --fix-ids
|
||||
```
|
||||
|
||||
There is no build step, linter config, or package install beyond `pip install -r requirements.txt`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Package vs CLI layer
|
||||
|
||||
`arma_modlist_tools/` is a pure library — no I/O side effects, no `sys.exit`, no `print`. All CLI scripts (`run.py`, `fetch_mods.py`, `link_mods.py`, etc.) sit at the project root and call into the package. New functionality goes in the package first, then a CLI script wraps it.
|
||||
|
||||
### Data flow
|
||||
|
||||
```
|
||||
modlist_html/*.html
|
||||
└─ parser.parse_modlist_dir()
|
||||
└─ compare.compare_presets()
|
||||
└─ comparison.json ←─ source of truth for groups + mod identity
|
||||
├─ fetcher.build_server_index() ←─ Caddy JSON API
|
||||
│ └─ fetcher.find_mod_folder() (steam_id first, name fallback)
|
||||
│ └─ downloads/{group}/@ModName/
|
||||
│ └─ linker.link_group()
|
||||
│ └─ arma_dir/@ModName (junction/symlink)
|
||||
└─ reporter.build_missing_report() → missing_report.json
|
||||
```
|
||||
|
||||
### Group naming convention
|
||||
|
||||
- `"shared"` — mods present in **all** compared presets
|
||||
- `"<preset_name>"` — mods unique to one preset (key from `comparison["unique"]`)
|
||||
|
||||
This group label is stored in `missing_report.json` per-mod so `sync_missing.py` knows where to place newly available mods without re-reading `comparison.json`.
|
||||
|
||||
### Server index structure
|
||||
|
||||
`build_server_index()` returns:
|
||||
```python
|
||||
{
|
||||
"by_steam_id": {"450814997": "https://server/@cba_a3/"}, # primary lookup
|
||||
"by_name": {"cbaa3": "https://server/@cba_a3/"}, # normalized fallback
|
||||
"folders": [...] # raw Caddy listing
|
||||
}
|
||||
```
|
||||
|
||||
`_normalize_name` strips `@`, lowercases, removes all non-alphanumeric: `"@CBA_A3"` → `"cbaa3"`. Used in both the index builder and every lookup.
|
||||
|
||||
### Junction / symlink critical rules
|
||||
|
||||
**Detection:** `os.path.islink()` returns `False` for Windows junctions. Always use `_is_junction()` from `linker.py`, which checks `st_file_attributes & 0x400` (`FILE_ATTRIBUTE_REPARSE_POINT`) on Windows.
|
||||
|
||||
**Removal:** Use `os.rmdir()` on Windows and `os.unlink()` on Linux. **Never** `shutil.rmtree()` — it follows the junction and deletes the target mod files.
|
||||
|
||||
**Creation:** `cmd /c mklink /J <link> <target>` on Windows, `os.symlink()` on Linux.
|
||||
|
||||
### check_names.py classification (two-pass)
|
||||
|
||||
Pass 1 collects raw `(server_name, local_steam_id)` for every disk folder.
|
||||
Pass 2 builds `ok_disk_names` — the set of disk names that already match the server exactly. Any MISMATCH whose proposed server name is in `ok_disk_names` is reclassified as `ID_COLLISION` (the local `meta.cpp` has a wrong `publishedid` that belongs to a different mod). This prevents false rename suggestions caused by shared/duplicate steam IDs on the server.
|
||||
|
||||
`--fix-ids` corrects `meta.cpp` using steam IDs from `comparison.json` (sourced from Steam Workshop URLs in the HTML presets) as the authoritative source.
|
||||
|
||||
## Python Version Compatibility
|
||||
|
||||
Minimum is Python **3.9**. All files that use `X | Y` union type annotations **must** have `from __future__ import annotations` as the first import. Without it, the `|` syntax raises `TypeError` at runtime on Python < 3.10. Every module in `arma_modlist_tools/` already has it; any new CLI script you add must include it too.
|
||||
|
||||
## Test Suite
|
||||
|
||||
`test_suite.py` uses a custom harness (no pytest/unittest dependency). Structure:
|
||||
|
||||
```python
|
||||
group("section name") # prints header
|
||||
test("description", callable) # runs fn, catches exceptions, tracks pass/fail
|
||||
skip("description", "reason") # marks skipped
|
||||
```
|
||||
|
||||
Tests that exercise the linker use `tempfile.TemporaryDirectory()` — never the real `arma_dir`. Tests that would require network calls mock `list_mod_files` with `unittest.mock.patch`.
|
||||
|
||||
## Key Files Not in Git
|
||||
|
||||
- `config.json` — credentials + paths (copy from `config.template.json`)
|
||||
- `downloads/` — downloaded mod files, can be several GB
|
||||
- `modlist_json/` — generated JSON output
|
||||
|
||||
The `.html` preset files in `modlist_html/` **are** tracked as example inputs.
|
||||
Reference in New Issue
Block a user