Metadata-Version: 2.4
Name: metin2-installer
Version: 0.1.2
Summary: Automated installer for Metin2 community systems — directive-driven source merge.
Author: metin2-installer contributors
License: MIT
License-File: LICENSE
Keywords: cpp,game-modding,installer,metin2,patcher,private-server
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Games/Entertainment
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Text Processing :: General
Requires-Python: >=3.12
Requires-Dist: charset-normalizer<4.0,>=3.0
Requires-Dist: questionary<3,>=2.1.1
Requires-Dist: rapidfuzz<4,>=3
Requires-Dist: rich<15,>=14
Requires-Dist: structlog<26,>=25
Requires-Dist: tree-sitter-cpp<0.24,>=0.23
Requires-Dist: tree-sitter<0.26,>=0.25
Requires-Dist: typer<0.25,>=0.24
Description-Content-Type: text/markdown

# metin2-installer

Automated installer for Metin2 community systems. Reads the directive files produced by
the community's "system" tutorials and applies them to a Client/Server source tree without
copy/paste mistakes. Backup-first, atomic-write, Windows-native.

**Status:** v0.1.0 (initial public release)
**Python:** 3.12+
**Platform:** Windows 11 (primary), Linux / macOS (supported)

## Install

Primary install path (recommended):

    uv tool install metin2-installer

Secondary install path:

    pipx install metin2-installer

Both produce a `metin2-install` binary on the PATH. No C toolchain required — every
runtime dependency ships prebuilt Windows wheels.

## Quick Start

Preview what the installer would do, without touching your source tree:

    metin2-install dry-run path/to/system-folder path/to/your/source-tree

Apply the system:

    metin2-install install path/to/system-folder path/to/your/source-tree

The `install` command always shows a summary + unified diff first, then prompts y/N
before writing a single byte.

## Directive Format

A "system" is a folder containing a `1. Svn/` subdirectory. Files under `1. Svn/`
are either **NEW** files (dropped in as-is) or **MODIFY** files carrying one or more
directives:

    // Search for:
    <code block to find in the target file>

    // Add after:
    <code block to insert immediately after the match>

Supported directive keywords:

| Keyword | Effect |
|---|---|
| `// Search for:` | Anchor block — located in the target file |
| `// Add after:` | Insert immediately after the anchor |
| `// Add before:` | Insert immediately before the anchor |
| `// Add for:` | Synonym of `Add after:` |
| `// Replace for:` | Replace the anchor with the following block |

Directives can carry an optional scope hint: `// Search for: (in CHARACTER::Update)`.
The parser is typo-tolerant — the real-world typos `// Seach for:` (missing `r`) and
`// Replace for` (missing colon) are accepted.

## Install Flow

1. **Scan** — walk `<system>/1. Svn/` recursively, classify each file as NEW or MODIFY.
2. **Plan** — for every MODIFY file, run the matcher and compute the new file content.
   Record a SHA-256 of the target file at plan time.
3. **Preview** — print a summary table and a unified diff for every modified file.
4. **Confirm** — prompt y/N (default No). Nothing is written until you confirm.
5. **Backup** — mirror every file about to change into
   `<target>/.metin2-installer/backups/<timestamp>-<system>/`.
6. **Apply** — write each file atomically. Re-hash before write; abort if the file
   changed on disk since the preview. Validate after write.
7. **Report** — print counts, backup path, and a machine-readable
   `apply.log.jsonl` inside the backup directory.

## Conflict UX

If a `// Search for:` block cannot be located in its target file, the installer pauses
and offers three choices:

- **Skip this block** — continue the run, record the skip in the final report.
- **Abort entire installation** — stop the run; no partial writes are left behind.
- **Edit the target file manually, then retry** — opens `$EDITOR` (or `notepad.exe` on
  Windows if `$EDITOR` is unset) at the anchor line. After the editor exits, the
  installer re-runs the matcher for the conflicted block only. If the re-planned match
  succeeds, the run continues. If it still fails, the two-choice (skip/abort) prompt
  is shown — no infinite retry.

**GUI editors note:** If you set `EDITOR=code`, VSCode forks and returns immediately,
which causes the re-plan to run on the unedited file. Use `EDITOR="code --wait"` for
VSCode, `EDITOR="notepad++ -multiInst -nosession"` for Notepad++, or rely on the
built-in `notepad.exe` fallback which blocks correctly.

## Backup Layout

Every apply run writes a timestamped backup mirror:

    <target>/.metin2-installer/
    ├── backups/
    │   └── 20260415T140530-<system-name>/
    │       ├── MANIFEST.json          # list of mirrored files
    │       ├── apply.log.jsonl        # structured per-directive events
    │       └── <mirror of every file about to change>
    └── installed.json                 # write-only manifest of installed systems

## Recovery

If an apply goes wrong, restore from the backup mirror with a single `robocopy`:

    robocopy <target>\.metin2-installer\backups\<timestamp>-<system>\ <target>\ /E /XD .metin2-installer

(Linux / macOS equivalent: `rsync -a --exclude .metin2-installer <backup>/ <target>/`.)

The backup path and full restore command are also printed to stderr whenever the tool
aborts mid-run.

## Flags

| Flag | Effect |
|---|---|
| `--yes`, `-y` | Bypass the y/N confirmation gate (alias for `--non-interactive`) |
| `--non-interactive` | Run without prompts |
| `--on-conflict=skip\|abort` | Pre-answer the conflict prompt |
| `--verbose`, `-v` | Echo individual block-apply events to stderr |
| `--no-color` | Suppress ANSI color output (also honors `NO_COLOR` env var) |
| `--backup-dir=<path>` | Override the default backup root |

Exit codes: `0` success · `1` user aborted · `2` parser/match error · `3` internal error.

## Safety

- **Nothing is written until you confirm.** The preview diff is always shown first.
- **Every write is atomic.** Temp file + `fsync` + `os.replace` — no half-written files.
- **Every write is backed up.** A mirror of every file about to change is created
  before the first byte is written.
- **Every write is validated.** The installer re-reads the file and asserts the new
  content is present; a failed assertion aborts the run.
- **Ctrl-C is safe.** The backup path is printed to stderr and no `.tmp` sidecar is
  left behind.

## Out of Scope (v1)

- Fuzzy matching / 3-way merge (exact match only — correctness over speed)
- Uninstall / idempotency detection (Phase 5 / v2)
- GUI (core is a library; a GUI can wrap it later)
- SQL execution / `.quest` / `.eix` / `.epk` toolchains (v3+)
- Automatic rollback on failure (backup + manual rollback is the v1 contract)

## License

MIT (see `pyproject.toml`).
