Skip to content
gustavoharielPublic

About

AI agent that auto-tunes Linux gaming per-game, decided ahead of launch

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

    ____            ____
   / __/_  ______  / __/___ _
  / /_/ / / / __ \/ /_/ __ `/
 / __/ /_/ / / / / __/ /_/ /
/_/  \__,_/_/ /_/_/  \__,_/

funfa

A Linux gaming optimizer powered by an LLM agent.

Tell it a game. It researches your hardware and the community fixes, drafts a tuning plan, asks you to approve it, then hands you a string to paste into Steam. You launch the game the way you always do.

Why · Features · How it works · Getting started · Usage · Configuration · Editing the agent · Roadmap

MIT License Bun 1.3.13 TypeScript ^6 Open issues Effect-TS

Table of Contents

Why this exists

Getting a particular game to behave on Linux usually means stitching together half a dozen sources: a ProtonDB report from 2023, a PCGamingWiki tweak, a GitHub issue on doitsujin/dxvk, a forum thread about a specific GPU driver bug. Every existing launcher — Lutris, Heroic, Bottles, Steam Tinker Launch — gives you somewhere to put the answer but not the answer itself.

funfa does that research pass for you up front, before you sit down to play. The output is a markdown plan plus a single launch-options string. Nothing runs in the hot path of your session.

Status: Phase-1 dogfood. One CLI command, one user, one validation game (Assassin's Creed Brotherhood, appid 48190).

back to top

Features

  • One-shot optimizer, never a wrapper — runs once, prints a Steam launch-options string, exits. No daemon, no shim, no funfa launch.
  • Skill-driven agent — the model's behavior lives in markdown files, not TypeScript. Edit a skill, rebuild, run. No code change needed to retune the agent.
  • Plan-then-accept consent — reads only probes your machine until you approve the plan. After acceptance, applies everything automatically (no per-step prompts).
  • Full auto-apply — installs Proton-GE, DXVK, community mods, and patches into your game prefix. Downloads verified and gated behind an SSRF-safe permission layer.
  • Steam-safe — never writes Steam config while Steam is running. Launch options and Proton version are printed as manual steps.
  • Community research built in — searches ProtonDB, PCGamingWiki, Reddit, and forums; reads broadly for consensus, verifies provenance before installing.

back to top

How it works

flowchart LR
    A["funfa 'Game Name'"] --> B[PHASE 0<br/>Check existing plans]
    B --> C[PHASE 1<br/>Investigate]
    C --> C1["load_skill(hardware)"]
    C --> C2["load_skill(game-research)"]
    C1 --> D["uname, glxinfo,<br/>/etc/os-release"]
    C2 --> E["ProtonDB, PCGamingWiki,<br/>Reddit, forums"]
    D --> F
    E --> F[PHASE 2<br/>Propose]
    F --> G["submit_plan(appid,<br/>plan_markdown)"]
    G --> H{Accept?}
    H -- No --> I[Plan saved to disk,<br/>declined. Exit.]
    H -- Yes --> J[PHASE 4<br/>Apply]
    J --> K["Locate game<br/>(libraryfolders.vdf)"]
    K --> L["apply_command: download,<br/>verify checksum, install"]
    L --> M[Print launch-options<br/>string. Done.]
Loading

The model orchestrates all four phases inside a single pi session call. submit_plan doubles as the consent gate — once accepted, apply_command runs with no further prompts.

back to top

Getting started

Prerequisites

  • Bun 1.3.13 (the runtime and package manager)
  • A pi credential: either a Claude Pro/Max or ChatGPT login, or a provider API key that pi recognizes
  • (Optional) A Parallel Search API key for live web search — without it, the agent falls back to reading canonical sources directly with fetch_page

Installation

git clone https://github.com/gustavohariel/funfa.git
cd funfa
bun install

# Compile the standalone binary (~71MB, includes Bun runtime)
bun run --filter cli compile   # → ./apps/cli/funfa

# (Optional) symlink somewhere on your PATH
ln -s "$PWD/apps/cli/funfa" ~/.local/bin/funfa

Authentication

funfa uses pi for LLM provider auth. Three ways to set it up:

1. In-process OAuth login (recommended):

funfa auth                  # interactive — picks a provider
funfa auth anthropic        # or specify: claude, chatgpt, copilot

This drives pi's OAuth flow in your browser. Credentials stored in ~/.pi/agent/auth.json.

2. Standalone pi login:

npx @earendil-works/pi-coding-agent
# then type: /login  (ChatGPT or Claude Pro/Max)

3. Provider API key: Set any key pi recognizes (e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY). funfa doesn't scan for keys itself — pi handles that.

back to top

Usage

funfa <game>           positional, free-form name (Steam, GOG, Epic, etc.)
  --json               stream JSON-lines progress events to stdout
  --bypass-confirms    skip the approval prompt (opt-in, never default)

funfa auth [provider]  log in to an LLM provider via OAuth (claude, chatgpt, copilot)

Example session:

$ funfa "Assassin's Creed Brotherhood"

  ── PHASE 1: Investigating hardware & game ──
  ✓ Kernel: 6.12.9-arch1-1
  ✓ GPU: AMD Radeon RX 6800 XT (RADV NAVI21)
  ✓ Distro: CachyOS
  ✓ Found 12 ProtonDB reports, 3 DXVK issues, 2 forum threads

  ── PHASE 2: Drafting plan ──
  ✓ Plan written to ~/.config/funfa/games/48190.md

  ── Plan Review ──
  Accept this plan? (y/N)

  ── PHASE 4: Applying changes ──
  ✓ Game located: ~/.steam/steam/steamapps/common/AC Brotherhood
  ✓ Proton prefix: ~/.steam/steam/compatdata/48190/pfx
  ↓ Downloading Proton-GE 9-25… done
  ✓ Extracted to ~/.steam/root/compatibilitytools.d/
  ↓ Applying DXVK 2.5.1… done

  ── Launch options (paste into Steam) ──
  PROTON_VERSION="GE-Proton9-25" DXVK_HUD=0 WINEDLLOVERRIDES="dinput8=n,b" %command%

Re-running on the same game runs the agent fresh. The plan file on disk is read-only from funfa's perspective and is kept even if you decline, so you always have a trace of what was proposed.

back to top

Configuration

Variable Purpose Default Required
FUNFA_PI_MODEL Override pi's default model — "<provider>/<model-id>", e.g. anthropic/claude-opus-4-5 pi's configured default optional
PARALLEL_API_KEY Live web search via the Parallel Search API — (falls back to fetch_page) optional
FUNFA_FETCH_ALLOWLIST Comma-separated domains to restrict fetch_page to (subdomains allowed) all public https hosts (SSRF-guarded) optional
FUNFA_DOWNLOAD_ALLOWLIST Comma-separated hosts to restrict apply_command downloads to all public https hosts (SSRF-guarded) optional
FUNFA_FAKE_HARDWARE Inject verified hardware facts and skip probes (dev / off-Linux testing) — optional
XDG_CONFIG_HOME Where plan files land ~/.config optional

Authentication is pi's concern, not funfa's. See the Authentication section above. Provider API keys (e.g. ANTHROPIC_API_KEY) are read by pi from the environment, not by funfa.

back to top

Editing the agent

The agent's behavior lives in markdown, not TypeScript:

apps/cli/src/skills/
  funfa/SKILL.md            ← base prompt / orchestration loop
  hardware/SKILL.md         ← which shell commands run for hardware probes
  game-research/SKILL.md    ← where to search, what to trust
  plan/SKILL.md             ← exact markdown shape for the final plan
  execute/SKILL.md          ← PHASE 4: game location, install commands

Edit a skill file, rebuild, run. No code change required to retune the agent. These files are inlined into the compiled binary at build time via Bun text imports, so the distributed funfa ships with its skills baked in.

To add a new skill: drop a <name>/SKILL.md and add one line in apps/cli/src/skills/registry.ts.

back to top

Project structure

apps/
  cli/                          # The funfa binary — single <game> command
    src/
      __tests__/                # All tests (cli, compile, plan, propose,
      │                         #   provider, review-plan, prompt, permission,
      │                         #   tools, auth, fetch-page, web-search-providers)
      agent/                    # pi auth, propose loop, permission gate
        tools/                  # submit_plan, load_skill, fetch_page,
                                #   web_search, apply_command
      core/                     # CLI surface (Effect CLI), plan I/O, deps
      services/                 # Stderr service tag
      skills/                   # Product skills — inlined into the binary
        funfa/SKILL.md
        hardware/SKILL.md
        game-research/SKILL.md
        plan/SKILL.md
        execute/SKILL.md
        registry.ts
      index.ts                  # Wiring + bin entry
  package.json

All source is in one Bun workspace at apps/cli. No @funfa/* internal packages — everything is local imports. The Plan shape lives in markdown only (plan/SKILL.md); the CLI never parses it.

back to top

Safety

funfa reads untrusted text (forum posts, wiki pages, GitHub issues) and runs local processes. The guardrails today:

  • Two-tier plans. Low-tier (env vars, launch args — fully reversible by clearing the Steam field) vs. high-tier (anything touching your prefix, installing binaries, editing the registry). funfa can now apply high-tier steps that live at the filesystem level (e.g. installing Proton-GE), but never silently.
  • Permission gate. Every apply goes through a permission gate that prompts you for consent (default deny) and blocks any download whose host is not on a pinned trusted-upstream allow-list, or that uses plain http. The apply_command tool runs a single argv array via execFile — no shell, no expansion.
  • SSRF guard. fetch_page and apply_command block private/local hosts (loopback, RFC-1918, link-local, cloud metadata, .local/.internal).
  • Steam stays hands-off. Steam-client state (launch options, Proton version) is never written from the CLI; those steps are printed for you to apply in Steam yourself.

Residual risk: a misaligned model could still misuse the read-only bash probe tool. OS-level confinement (bubblewrap) around apply_command is future work.

--bypass-confirms auto-allows applies. It is opt-in, for scripting only, never default.

back to top

Roadmap

  • Phase 0 — manual prototype: one hard game tuned by hand to learn what "good" looks like
  • Phase 1 — CLI MVP (you're here), dogfooded on one game, with gated execution of the plan's filesystem steps
  • Phase 2 — Decky Loader plugin: Game Mode UI shells out to the CLI
  • Phase 3 — auto-detection, multi-launcher integration, an OS-level sandbox (bubblewrap) around apply_command, optional community plan sharing

The bar for keeping the project alive: the author reaches for funfa when setting up a new game on Linux, instead of opening four browser tabs. If that doesn't hold after a few weekends, kill it. Cheap to do — no Decky plugin to throw away yet.

back to top

Stack

Runtime Bun 1.3.13
Language TypeScript ^6
Monorepo / build Turborepo + tsdown
Agent loop embedded pi — createAgentSession
Effects / errors Effect-TS 3.21
CLI framework @effect/cli
User prompts @inquirer/prompts
Web search Parallel Search API (optional)
Lint / format Ultracite (Biome preset)

back to top

Scripts

Command What it does
bun install Install all dependencies
bun run dev:cli CLI in hot-reload mode
bun run test Full test suite (bun test across all apps)
bun run check-types TypeScript typecheck (tsc --noEmit)
bun run check Ultracite lint + format check
bun run fix Ultracite lint + format fix
bun run --filter cli compile Produce the standalone apps/cli/funfa binary

See CLAUDE.md for the deeper architecture notes and commit conventions.

back to top

Contributing

PRs welcome. For anything non-trivial, open an issue first — CLAUDE.md documents the architectural commitments worth preserving:

  • One-shot optimizer, never a launch wrapper
  • Skill-driven agent loop (edit markdown, not code)
  • Plan-then-accept consent model
  • Steam config stays hands-off

back to top

License

MIT — see LICENSE.

back to top

Credits

funfa stands on prior art it does not try to replace:

README structure adapted from othneildrew/Best-README-Template.

back to top

About

AI agent that auto-tunes Linux gaming per-game, decided ahead of launch

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages