This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
dosctl is a Python CLI tool for managing and playing DOS games via DOSBox. It downloads games from Archive.org collections (currently Total DOS Collection Release 14), manages installations, and launches them through DOSBox. Supports Linux, macOS, and Windows.
# Install in dev mode (creates 'dosctl' CLI entry point)
pip install -e ".[dev]"
# Run all tests
python -m pytest
# Run a single test file
python -m pytest tests/test_config.py
# Run a specific test
python -m pytest tests/test_config.py::test_function_name
# Build package
python -m build- Entry point:
src/dosctl/main.py— defines thecliClick group with subcommands: list, search, play, inspect, delete, refresh, net, alias, info, version - Commands:
src/dosctl/commands/— each file is one subcommand (net.py and alias.py are Click subgroups)
- Click subgroup with two subcommands:
hostandjoin host— starts DOSBox as IPX server. LAN mode by default;--internet/-ienables UPnP port mapping, public IP detection, and discovery code generation. Additional flags:--public-ip/-I(skip IP detection),--no-upnp/-U(skip UPnP),--port/-p,--configure/-c,--no-exec/-n(DOSBox prompt only)join— connects to an IPX server. Accepts raw IP (LAN) or discovery code (internet). Usesresolve_host()to auto-detect format. Flags:--port/-p,--configure/-c_setup_internet_hosting()orchestrates UPnP → CGNAT detection → public IP → discovery code display_prepare_game()and_launch_net_game()are shared helpers for game installation and DOSBox launch
Most commands are wrapped with @ensure_cache, which automatically creates directories, initializes the game collection, and loads/downloads the game cache before passing the collection to the command handler. This is the central orchestration mechanism.
base.py—BaseCollectionABC defining the collection interfacearchive_org.py—ArchiveOrgCollectionbase +TotalDOSCollectionRelease14concrete classfactory.py— creates collection instances- Game IDs are 8-character SHA1 hash prefixes derived from the game filename
PlatformBaseABC for OS-specific paths and DOSBox detectionUnixPlatform,MacOSPlatform,WindowsPlatformimplementationsPlatformFactoryandget_platform()singleton
DOSBoxLauncherABC andStandardDOSBoxLauncherusing subprocess.Popenget_dosbox_launcher()singleton factory,is_dosbox_available(),is_dosbox_installed()- Supports IPX networking via the
ipxoption (acceptsIPXServerConfigorIPXClientConfig), which injects-conf ipx.confandIPXNETcommands
IPXServerConfig/IPXClientConfigdataclasses withto_dosbox_command()methodsget_local_ip()helper for LAN IP detectionget_public_ip()helper for public IP detection via external servicesis_cgnat_address()helper to detect CGNAT/private WAN IPs (Starlink, etc.)
- Encodes IPv4 + port into human-friendly codes like
DOOM-3KF8A - 256-word list maps first IP byte to a word; remaining 3 bytes as base36
encode_discovery_code(),decode_discovery_code(),resolve_host()(auto-detects raw IP vs code)- Word list stored in
src/dosctl/lib/wordlist.txt(8 words per line)
- Stdlib-only UPnP IGD implementation for automatic router port forwarding
- SSDP discovery, SOAP port mapping, external IP retrieval
UPnPPortMapperclass with atexit cleanup of registered mappings
- Click subgroup with three subcommands:
set,remove,list set ALIAS_NAME GAME_ID— creates/updates an alias; validates format (lowercase letters, digits, hyphens) and that the game existsremove ALIAS_NAME— deletes an aliaslist— shows all aliases with their game IDs and names
dosctl info GAME_ID|ALIAS— shows game metadata (name, ID, year, alias if set, status)- Status: "Not downloaded", "Downloaded", or "Installed"; shows archive/install path and saved default command
config.py— platform-aware directory paths (config, data, collections, downloads, installed); also definesIPX_CONF_PATHandDEFAULT_COLLECTION_SOURCElib/game.py— game download, extraction, and installationlib/aliases.py— alias storage inaliases.json;set_alias(),remove_alias(),remove_aliases_for_game_id(),list_aliases(),resolve_game_id()(resolves alias or passes through raw game ID)lib/config_store.py— persists chosen executable/command per game inplay_config.json(migrated from oldrun_config.json)lib/executables.py— finds .exe/.com/.bat files in game directories; shared executable selection/prompting logic used by both play and net commandslib/display.py— terminal display formatting for game listings
- Commits: Conventional Commits format.
feat:= minor bump,fix:= patch bump,chore:= no bump. See CONTRIBUTING.md. - Releases: Automated via semantic-release on push to
main. Updates version in bothpyproject.tomlandsrc/dosctl/__init__.py. - Python: Requires >=3.8. Dependencies: click, requests, tqdm.
- Testing: pytest with unittest.mock. Tests use temporary directories for isolation.
- Patterns: Factory pattern for platform/collection/launcher. ABC for extensibility. Singletons for platform and launcher instances.