Compare commits
1 Commits
main
...
step35/721
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c684d1286 |
@@ -38,22 +38,6 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Guard: reject PRs with zero file changes"
|
||||
run: |
|
||||
CHANGED=$(git diff --name-only origin/main...HEAD | wc -l | tr -d ' ')
|
||||
echo "Changed files: $CHANGED"
|
||||
if [ "$CHANGED" -eq 0 ]; then
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════"
|
||||
echo " BLOCKED: PR contains zero file changes."
|
||||
echo " This indicates rubber-stamping — approving without"
|
||||
echo " actually making any modifications."
|
||||
echo " Make real changes before requesting review."
|
||||
echo "═══════════════════════════════════════════════════"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ PR has $CHANGED changed file(s)."
|
||||
|
||||
- name: Validate Python syntax
|
||||
run: |
|
||||
FAIL=0
|
||||
|
||||
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -30,7 +30,3 @@ timmy-config/ @perplexity
|
||||
|
||||
# Owner gates
|
||||
hermes-agent/ @Timmy
|
||||
|
||||
# SOUL.md requires review from @Timmy (canonical location: timmy-home/SOUL.md)
|
||||
SOUL.md @Timmy
|
||||
timmy-home/SOUL.md @Timmy
|
||||
|
||||
@@ -102,20 +102,6 @@ Example: `feat/mnemosyne-memory-decay`
|
||||
|
||||
**Never** create a duplicate module at the repo root (e.g., `mnemosyne/` when `nexus/mnemosyne/` already exists). Check `FEATURES.yaml` manifests for the canonical path.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Identity File Canonical Locations
|
||||
|
||||
To avoid duplicate PRs and identity drift, SOUL.md has a single source of truth:
|
||||
|
||||
| File | Canonical Location |
|
||||
|------|-------------------|
|
||||
| `SOUL.md` (Timmy's identity/values) | `timmy-home` repository (`timmy-home/SOUL.md`) |
|
||||
|
||||
- The-nexus contains a **pointer file** (`SOUL.md`) that references the canonical location.
|
||||
- Never create or modify SOUL.md in `timmy-config` or any other repository.
|
||||
- See policy: [docs/soul-canonical-location.md](docs/soul-canonical-location.md) ✅ DECIDED (#1443)
|
||||
---
|
||||
|
||||
## Feature Manifests
|
||||
|
||||
@@ -114,6 +114,44 @@ After setup, the key paths are:
|
||||
|
||||
---
|
||||
|
||||
## GOG Version Support
|
||||
|
||||
The runtime also supports the GOG version of Bannerlord (no Steam required).
|
||||
|
||||
### Installing GOG Version
|
||||
|
||||
1. Download the GOG Windows installer from your GOG library (BannerlordSetup.exe)
|
||||
2. In Whisky, open the Bannerlord bottle
|
||||
3. Run the GOG installer inside the bottle
|
||||
4. Install to: `C:\Program Files (x86)\GOG Games\Mount & Blade II Bannerlord\`
|
||||
|
||||
The GOG executable will appear at:
|
||||
```
|
||||
drive_c/Program Files (x86)/GOG Games/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe
|
||||
```
|
||||
|
||||
### Launching GOG Version
|
||||
|
||||
```python
|
||||
from bannerlord_runtime import BannerlordRuntime
|
||||
|
||||
rt = BannerlordRuntime()
|
||||
# Auto-select: Steam if present, else GOG
|
||||
rt.launch(source="auto")
|
||||
|
||||
# Force GOG
|
||||
rt.launch(source="gog")
|
||||
```
|
||||
|
||||
Or from the command line:
|
||||
```bash
|
||||
python -m nexus.bannerlord_runtime --source gog
|
||||
```
|
||||
|
||||
The verification script (below) detects both Steam and GOG installs.
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
Run the verification script to confirm the runtime is operational:
|
||||
|
||||
@@ -1,23 +1,6 @@
|
||||
# SOUL.md Canonical Location Policy
|
||||
|
||||
**Issue:** #1443 — decide: Establish SOUL.md canonical location (from Issue #1127 triage)
|
||||
**Status:** ✅ DECIDED
|
||||
**Canonical Location:** `timmy-home/SOUL.md`
|
||||
|
||||
## Decision
|
||||
|
||||
**SOUL.md canonical location is `timmy-home/SOUL.md`.**
|
||||
|
||||
This decision was made based on:
|
||||
1. **Existing Practice:** PR #580 was approved in timmy-home
|
||||
2. **Repository Structure:** timmy-home contains core identity files
|
||||
3. **CLAUDE.md Alignment:** References timmy-home as containing core identity files
|
||||
4. **Separation of Concerns:**
|
||||
- `timmy-home`: Core identity, values, and configuration
|
||||
- `timmy-config`: Operational configuration and tools
|
||||
- `the-nexus`: 3D world and visualization
|
||||
|
||||
---
|
||||
**Issue:** #1127 - Perplexity Evening Pass triage identified duplicate SOUL.md files causing duplicate PRs.
|
||||
|
||||
## Current State
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ class RuntimePaths:
|
||||
bottle_root: Path = field(init=False)
|
||||
drive_c: Path = field(init=False)
|
||||
steam_exe: Path = field(init=False)
|
||||
gog_exe: Path = field(init=False)
|
||||
bannerlord_exe: Path = field(init=False)
|
||||
installer_path: Path = field(init=False)
|
||||
|
||||
@@ -43,6 +44,12 @@ class RuntimePaths:
|
||||
self.steam_exe = (
|
||||
base / "drive_c/Program Files (x86)/Steam/Steam.exe"
|
||||
)
|
||||
# GOG install path (standard Windows Program Files location inside Wine)
|
||||
self.gog_exe = (
|
||||
base
|
||||
/ "drive_c/Program Files (x86)/GOG Games/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe"
|
||||
)
|
||||
# Steam default
|
||||
self.bannerlord_exe = (
|
||||
base
|
||||
/ "drive_c/Program Files (x86)/Steam/steamapps/common"
|
||||
@@ -83,6 +90,7 @@ class RuntimeStatus:
|
||||
"bottle_exists": self.bottle_exists,
|
||||
"drive_c_populated": self.drive_c_populated,
|
||||
"steam_installed": self.steam_installed,
|
||||
"gog_installed": self.gog_installed,
|
||||
"bannerlord_installed": self.bannerlord_installed,
|
||||
"gptk_available": self.gptk_available,
|
||||
"macos_version": self.macos_version,
|
||||
@@ -150,10 +158,17 @@ class BannerlordRuntime:
|
||||
if not status.steam_installed:
|
||||
status.warnings.append("Steam (Windows) not installed in bottle")
|
||||
|
||||
# Bannerlord
|
||||
status.bannerlord_installed = self.paths.bannerlord_exe.exists()
|
||||
# GOG (Windows)
|
||||
status.gog_installed = self.paths.gog_exe.exists()
|
||||
if not status.gog_installed:
|
||||
status.warnings.append("GOG version not installed in bottle")
|
||||
|
||||
# Bannerlord — detect either Steam or GOG install
|
||||
status.bannerlord_installed = (
|
||||
self.paths.bannerlord_exe.exists() or self.paths.gog_exe.exists()
|
||||
)
|
||||
if not status.bannerlord_installed:
|
||||
status.warnings.append("Bannerlord not installed")
|
||||
status.warnings.append("Bannerlord executable not found (Steam or GOG)")
|
||||
|
||||
# GPTK/D3DMetal
|
||||
whisky_support = Path.home() / "Library/Application Support/Whisky"
|
||||
@@ -165,35 +180,62 @@ class BannerlordRuntime:
|
||||
|
||||
return status
|
||||
|
||||
def launch(self, with_steam: bool = True) -> subprocess.Popen | None:
|
||||
def launch(self, source: str = "auto") -> subprocess.Popen | None:
|
||||
"""
|
||||
Launch Bannerlord via Whisky.
|
||||
|
||||
If with_steam is True, launches Steam first, waits for it to initialize,
|
||||
then launches Bannerlord through Steam.
|
||||
Args:
|
||||
source: "steam" | "gog" | "auto"
|
||||
"steam" — launch via Steam (steam://rungameid)
|
||||
"gog" — launch GOG executable directly
|
||||
"auto" — choose Steam if available, else GOG
|
||||
|
||||
Returns:
|
||||
subprocess.Popen for the launched process, or None on failure.
|
||||
"""
|
||||
status = self.check()
|
||||
if not status.ready:
|
||||
log.error("Runtime not ready: %s", "; ".join(status.errors or status.warnings))
|
||||
return None
|
||||
|
||||
if with_steam:
|
||||
# Resolve source
|
||||
if source == "auto":
|
||||
source = "steam" if status.steam_installed else "gog"
|
||||
|
||||
if source == "steam":
|
||||
if not status.steam_installed:
|
||||
log.error("Steam not installed; cannot launch via Steam")
|
||||
return None
|
||||
log.info("Launching Steam (Windows) via Whisky...")
|
||||
steam_proc = self._run_exe(str(self.paths.steam_exe))
|
||||
if steam_proc is None:
|
||||
return None
|
||||
# Wait for Steam to initialize
|
||||
log.info("Waiting for Steam to initialize (15s)...")
|
||||
time.sleep(15)
|
||||
|
||||
# Launch Bannerlord via steam://rungameid/
|
||||
log.info("Launching Bannerlord via Steam protocol...")
|
||||
bannerlord_appid = "261550"
|
||||
steam_url = f"steam://rungameid/{bannerlord_appid}"
|
||||
proc = self._run_exe(str(self.paths.steam_exe), args=[steam_url])
|
||||
if proc:
|
||||
log.info("Bannerlord launch command sent (PID: %d)", proc.pid)
|
||||
return proc
|
||||
log.info("Launching Bannerlord via Steam protocol...")
|
||||
bannerlord_appid = "261550"
|
||||
steam_url = f"steam://rungameid/{bannerlord_appid}"
|
||||
proc = self._run_exe(str(self.paths.steam_exe), args=[steam_url])
|
||||
if proc:
|
||||
log.info("Bannerlord launch command sent (PID: %d)", proc.pid)
|
||||
return proc
|
||||
|
||||
elif source == "gog":
|
||||
if not status.gog_installed:
|
||||
log.error("GOG version not installed; cannot launch via GOG")
|
||||
return None
|
||||
log.info("Launching Bannerlord (GOG) via wine64-preloader...")
|
||||
# Use GOG exe directly
|
||||
exe_path = str(self.paths.gog_exe)
|
||||
proc = self._run_exe(exe_path)
|
||||
if proc:
|
||||
log.info("Bannerlord (GOG) launched (PID: %d)", proc.pid)
|
||||
return proc
|
||||
|
||||
else:
|
||||
log.error("Invalid launch source: %s (must be 'steam', 'gog', or 'auto')", source)
|
||||
return None
|
||||
|
||||
def _run_exe(self, exe_path: str, args: list[str] | None = None) -> subprocess.Popen | None:
|
||||
"""Run a Windows executable through Whisky's wine64-preloader."""
|
||||
@@ -257,7 +299,19 @@ class BannerlordRuntime:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(message)s")
|
||||
rt = BannerlordRuntime()
|
||||
|
||||
parser = argparse.ArgumentParser(description="Bannerlord runtime manager (Whisky/Wine on macOS)")
|
||||
parser.add_argument("--source", choices=["steam", "gog", "auto"], default="auto",
|
||||
help="Which version to launch: steam, gog, or auto (default)")
|
||||
parser.add_argument("--launch", action="store_true",
|
||||
help="Launch Bannerlord after status check")
|
||||
args = parser.parse_args()
|
||||
|
||||
status = rt.check()
|
||||
print(json.dumps(status.to_dict(), indent=2))
|
||||
print(json.dumps(status.to_dict(), indent=2, sort_keys=True))
|
||||
|
||||
if args.launch:
|
||||
rt.launch(source=args.source)
|
||||
|
||||
@@ -61,13 +61,26 @@ else
|
||||
check "Steam (Windows) installed" "FAIL" "not found at expected path"
|
||||
fi
|
||||
|
||||
# ── Check 5: Bannerlord executable ───────────────────────────────
|
||||
BANNERLORD_EXE="$BOTTLE_DIR/drive_c/Program Files (x86)/Steam/steamapps/common/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe"
|
||||
if [[ -f "$BANNERLORD_EXE" ]]; then
|
||||
EXE_SIZE=$(stat -f%z "$BANNERLORD_EXE" 2>/dev/null || echo "?")
|
||||
check "Bannerlord executable found" "PASS" "size: $EXE_SIZE bytes"
|
||||
# ── Check 4b: GOG version ──────────────────────────────────────────
|
||||
GOG_EXE="$BOTTLE_DIR/drive_c/Program Files (x86)/GOG Games/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe"
|
||||
if [[ -f "$GOG_EXE" ]]; then
|
||||
check "GOG version installed" "PASS" "$GOG_EXE"
|
||||
else
|
||||
check "Bannerlord executable found" "FAIL" "not installed yet"
|
||||
check "GOG version installed" "WARN" "not found (optional if using Steam)"
|
||||
fi
|
||||
|
||||
# ── Check 5: Bannerlord executable ───────────────────────────────
|
||||
# Check both Steam and GOG locations
|
||||
BANNERLORD_STEAM="$BOTTLE_DIR/drive_c/Program Files (x86)/Steam/steamapps/common/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe"
|
||||
BANNERLORD_GOG="$BOTTLE_DIR/drive_c/Program Files (x86)/GOG Games/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe"
|
||||
if [[ -f "$BANNERLORD_STEAM" ]]; then
|
||||
EXE_SIZE=$(stat -f%z "$BANNERLORD_STEAM" 2>/dev/null || echo "?")
|
||||
check "Bannerlord executable found" "PASS" "Steam — size: $EXE_SIZE bytes"
|
||||
elif [[ -f "$BANNERLORD_GOG" ]]; then
|
||||
EXE_SIZE=$(stat -f%z "$BANNERLORD_GOG" 2>/dev/null || echo "?")
|
||||
check "Bannerlord executable found" "PASS" "GOG — size: $EXE_SIZE bytes"
|
||||
else
|
||||
check "Bannerlord executable found" "FAIL" "not installed yet (neither Steam nor GOG)"
|
||||
fi
|
||||
|
||||
# ── Check 6: GPTK/D3DMetal presence ──────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user