Skip to content

Latest commit

 

History

History
101 lines (77 loc) · 5.95 KB

File metadata and controls

101 lines (77 loc) · 5.95 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

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.

Development Commands

# 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

Architecture

CLI Layer (Click framework)

  • Entry point: src/dosctl/main.py — defines the cli Click 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)

Net Command (src/dosctl/commands/net.py)

  • Click subgroup with two subcommands: host and join
  • host — starts DOSBox as IPX server. LAN mode by default; --internet/-i enables 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). Uses resolve_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

Core Pattern: @ensure_cache decorator (src/dosctl/lib/decorators.py)

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.

Collection Backend (src/dosctl/collections/)

  • base.pyBaseCollection ABC defining the collection interface
  • archive_org.pyArchiveOrgCollection base + TotalDOSCollectionRelease14 concrete class
  • factory.py — creates collection instances
  • Game IDs are 8-character SHA1 hash prefixes derived from the game filename

Platform Abstraction (src/dosctl/lib/platform.py)

  • PlatformBase ABC for OS-specific paths and DOSBox detection
  • UnixPlatform, MacOSPlatform, WindowsPlatform implementations
  • PlatformFactory and get_platform() singleton

DOSBox Launcher (src/dosctl/lib/dosbox.py)

  • DOSBoxLauncher ABC and StandardDOSBoxLauncher using subprocess.Popen
  • get_dosbox_launcher() singleton factory, is_dosbox_available(), is_dosbox_installed()
  • Supports IPX networking via the ipx option (accepts IPXServerConfig or IPXClientConfig), which injects -conf ipx.conf and IPXNET commands

IPX Networking (src/dosctl/lib/network.py)

  • IPXServerConfig / IPXClientConfig dataclasses with to_dosbox_command() methods
  • get_local_ip() helper for LAN IP detection
  • get_public_ip() helper for public IP detection via external services
  • is_cgnat_address() helper to detect CGNAT/private WAN IPs (Starlink, etc.)

Discovery Codes (src/dosctl/lib/discovery.py)

  • 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)

UPnP Port Mapping (src/dosctl/lib/upnp.py)

  • Stdlib-only UPnP IGD implementation for automatic router port forwarding
  • SSDP discovery, SOAP port mapping, external IP retrieval
  • UPnPPortMapper class with atexit cleanup of registered mappings

Alias Command (src/dosctl/commands/alias.py)

  • 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 exists
  • remove ALIAS_NAME — deletes an alias
  • list — shows all aliases with their game IDs and names

Info Command (src/dosctl/commands/info.py)

  • 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

Other Key Modules

  • config.py — platform-aware directory paths (config, data, collections, downloads, installed); also defines IPX_CONF_PATH and DEFAULT_COLLECTION_SOURCE
  • lib/game.py — game download, extraction, and installation
  • lib/aliases.py — alias storage in aliases.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 in play_config.json (migrated from old run_config.json)
  • lib/executables.py — finds .exe/.com/.bat files in game directories; shared executable selection/prompting logic used by both play and net commands
  • lib/display.py — terminal display formatting for game listings

Conventions

  • 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 both pyproject.toml and src/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.