Add simulator replica, persistence, planet scan, README; split empire state out of the repo

- tools/simulate.py + puga/simulate.py: replica of PRUNplanner's simulator (flows and
  efficiency verified against screenshots), reports real new capex (planned minus built)
- tools/scan.py: staffing variants, freight, HQ/experts, --planet mode, demolish-later,
  --min-n as a pure market-size filter, --json output
- tools/history.py, tools/persistence.py: margin history and short-horizon payback checks
- tools/plan_push.py: guarded delete; tools/state.py: syncs to empire/
- README with features and setup; CLAUDE.md made generic
- Own-empire material (profile, state, plans, notes) moved to gitignored empire/;
  generic examples in plans/examples and state/company.example.yaml

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 00:11:02 +02:00
co-authored by Claude Sonnet 5
parent 7a538cb300
commit 3bbf524ebb
26 changed files with 778 additions and 259 deletions
+12 -8
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Sync state/company.yaml from live FIO (own data via FIO_REST_KEY) and show it.
"""Sync empire/state/company.yaml from live FIO (own data via FIO_REST_KEY) and show it.
tools/state.py sync # overwrite live fields (buildings, production efficiency, storage, ships, cash, permits)
tools/state.py show
Requires the FIO extension to have uploaded recently; check `as_of`. Manual keys (company, notes) are preserved."""
@@ -8,23 +8,27 @@ from collections import Counter
from pathlib import Path
import yaml
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from puga import ROOT, fio
from puga import ROOT, config, fio
P = ROOT / "state" / "company.yaml"
P = config.EMPIRE_DIR / "state" / "company.yaml" # gitignored
def sync():
u = fio.me()
P.parent.mkdir(parents=True, exist_ok=True)
st = yaml.safe_load(P.read_text()) if P.exists() else {}
company = fio.private(f"/company/code/{(st.get('company') or {}).get('ticker', 'GBI')}", ttl=0)
code = (st.get('company') or {}).get('ticker') or config.get('COMPANY_CODE')
if not code:
sys.exit('set COMPANY_CODE=<your company code> in .env (first sync), or put company: {ticker: ..} in empire/state/company.yaml')
company = fio.own(f"/company/code/{code}", ttl=0)
for stale in ("cash_aic",):
st.pop(stale, None)
st["as_of"] = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%MZ")
st["cash"] = {b["Currency"]: b["Amount"] for b in company["Balances"] if b["Amount"]}
sites = fio.private(f"/sites/{u}", ttl=0)
prod = fio.private(f"/production/{u}", ttl=0)
stores = fio.private(f"/storage/{u}", ttl=0)
ships = fio.private(f"/ship/ships/{u}", ttl=0)
sites = fio.own(f"/sites/{u}", ttl=0)
prod = fio.own(f"/production/{u}", ttl=0)
stores = fio.own(f"/storage/{u}", ttl=0)
ships = fio.own(f"/ship/ships/{u}", ttl=0)
st["permits"] = {"used": sites[0]["InvestedPermits"] if sites else 0, "total": sites[0]["MaximumPermits"] if sites else 0}
old = {b["planet"]: b for b in st.get("bases", [])}
bases = []