# wheriflo

> **🧪 Beta.** wheriflo is under active development — expect rough edges and
> please [report issues](https://github.com/harrisonhoffman1/wheriflo/issues).

Build, test, and play [Wherigo](https://www.wherigo.com/) cartridges from the command line — with an MCP server for AI-assisted cartridge creation.

### Describe it. AI builds it. Go play it.

![What is Wheriflo?](docs/images/01-what-is-wheriflo.png)

## Install

**One command, any AI client** — installs wheriflo and configures every AI
client found on your machine (Claude Desktop, Claude Code, VS Code/Copilot,
Cursor, OpenAI Codex):

**Mac / Linux:**
```bash
curl -LsSf https://raw.githubusercontent.com/harrisonhoffman1/wheriflo/main/scripts/install.sh | sh
```

**Windows (PowerShell):**
```powershell
powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/harrisonhoffman1/wheriflo/main/scripts/install.ps1 | iex"
```

Already have Python? You can install with pip and run the configurator
yourself:

```bash
pip install wheriflo
wheriflo setup        # detects your AI clients and configures them
```

`wheriflo setup` merges into existing config files (never overwrites other
servers), backs up anything it touches, and is safe to re-run. Use
`--dry-run` to preview and `--client KEY` to target one client.

Verify it works:
```bash
wheriflo --version
```

> **Note:** If you have multiple Python environments, `wheriflo --version` may
> report a different version than what you just installed (e.g. a previously
> installed version in user site-packages). Use `pip show wheriflo` to confirm
> which version is active.

### Running with `uvx` in sandboxed environments

Some AI coding agents and CI sandboxes only allow writes inside the workspace or
temporary directories. If `uvx` cannot write to its default directories under
your home folder, it may fail before `wheriflo` starts. Set both `UV_CACHE_DIR`
and `UV_TOOL_DIR` to writable paths:

```bash
UV_CACHE_DIR=.uv-cache UV_TOOL_DIR=.uv-tools uvx --from wheriflo wheriflo compile path/to/Cartridge.lua
```

Use absolute paths if your MCP client starts outside the workspace.

## Quick Start

![CLI Commands](docs/images/05-cli-commands.png)

```bash
# Configure your AI clients to use wheriflo (one-time)
wheriflo setup

# Scaffold a new geocache cartridge (default type)
wheriflo init "My Adventure" --author "Jane" --location 47.6,-122.3 --find-location 47.601,-122.301

# Scaffold a standalone game (no physical cache)
wheriflo init "Mystery Game" --author "Jane" --location 47.6,-122.3 --activity WherigoGame

# Test in browser (auto-reloads when file changes)
wheriflo play my-adventure/MyAdventure.lua --watch

# Build for release
wheriflo build my-adventure/MyAdventure.lua

# Store Groundspeak credentials securely (one-time setup)
wheriflo credentials set

# Compile via the official Groundspeak API
wheriflo compile my-adventure/MyAdventure.lua

# Review and edit cartridge text — iOS screen previews, variable inspector, inline editing
wheriflo edit my-adventure/MyAdventure.lua

# View gameplay flow map with jump-to-section testing
wheriflo flow my-adventure/MyAdventure.lua

# Publish to wherigo.com (opens browser, copies .gwz path)
wheriflo publish my-adventure/MyAdventure.lua

# Validate a compiled cartridge
wheriflo validate MyAdventure.gwc

# Check version
wheriflo --version
```

## How It Works

![Build Pipeline](docs/images/02-build-pipeline.png)

## Groundspeak Compiler

The toolkit can compile your cartridge through the **official Groundspeak
(wherigo.com) compiler API**. This is the definitive compatibility test — if
Groundspeak accepts it, the cartridge will work on all Wherigo players.

### Setting up credentials

You need a free [wherigo.com](https://www.wherigo.com/) account.

**Option 1: System keyring (recommended)** — credentials are encrypted by your OS:

```bash
pip install keyring            # if not already installed
wheriflo credentials set       # prompts for username and password
wheriflo credentials check     # verify credentials are stored
```

This uses Windows Credential Manager, macOS Keychain, or Linux Secret Service.

**Option 2: Environment variables** — for CI/automation or if keyring is unavailable:

```bash
# Linux / Mac
export WHERIGO_USERNAME=your_username
export WHERIGO_PASSWORD=your_password

# Windows (PowerShell)
$env:WHERIGO_USERNAME = "your_username"
$env:WHERIGO_PASSWORD = "your_password"
```

**Option 3: Provide at compile time** — when using the MCP server, the AI
assistant can ask for your credentials and pass them directly to
`wheriflo_compile`. They are never stored.

For MCP clients, you can also add credentials to your server configuration
(note: these are stored as plain text in the config file):

```json
{
    "servers": {
        "wheriflo": {
            "command": "uvx",
            "args": ["wheriflo", "mcp"],
            "env": {
                "WHERIGO_USERNAME": "your_username",
                "WHERIGO_PASSWORD": "your_password"
            }
        }
    }
}
```

> **Security:** Credentials are resolved in order: runtime parameters →
> environment variables → system keyring. They are never logged, never
> included in error messages, and the connection to wherigo.com uses HTTPS.
> The SOAP request body is built with XML-safe escaping to prevent injection.

> **Sandbox:** Cartridge Lua runs in a restricted lupa (Lua 5.1) sandbox when
> you `play`/test a cartridge. The Python bridge is disabled
> (`register_eval=False`, `register_builtins=False`), an attribute filter
> blocks access to private/dunder attributes of Python objects, and `io`,
> `os`, `debug`, `loadfile`, `dofile`, `loadstring`, `load`, and
> `package.loadlib`/`loaders` are removed before cartridge code runs. See
> [SECURITY.md](SECURITY.md) for details.

## MCP Server

The toolkit includes an MCP server for use with AI coding assistants
(GitHub Copilot, Claude Desktop, Claude Code, Cursor, Zed, etc.).

### Tools

![MCP Server Tools](docs/images/04-mcp-server.png)

| Tool | Description |
|------|-------------|
| `wheriflo_init` | Scaffold a new cartridge project |
| `wheriflo_build` | Compile .lua → .gwc/.gwz (local) |
| `wheriflo_compile` | Compile via official Groundspeak API |
| `wheriflo_validate` | Validate a .gwc binary |
| `wheriflo_lint` | Check .lua for common mistakes |
| `wheriflo_play` | Load cartridge + start web player |
| `wheriflo_get_state` | Inspect current game state |
| `wheriflo_teleport` | Enter a zone (trigger OnEnter) |
| `wheriflo_leave_zone` | Exit a zone (trigger OnExit) |
| `wheriflo_set_position` | Set player GPS coordinates |
| `wheriflo_respond` | Dismiss message or answer prompt |
| `wheriflo_autoplay` | Run a sequence of play-test steps |
| `wheriflo_get_resource` | Read API reference, design guide, or template |
| `wheriflo_edit` | Review/extract all cartridge text |
| `wheriflo_flow` | Analyze cartridge gameplay flow graph |
| `wheriflo_publish` | Open wherigo.com to upload or update |
| `wheriflo_reset` | Full state wipe and restart |
| `wheriflo_stop` | Stop web player and free the port |

### Resources

- `wherigo://api-reference` — Lua API docs
- `wherigo://design-guide` — Design best practices
- `wherigo://lua-template` — Starter template

### Guides

- [Getting Started](docs/getting-started.md) — Installation, setup, first cartridge
- [Flow Map Guide](docs/flow-map-guide.md) — Interactive gameplay flow visualization, jump-to-section testing, coverage tracking

### Prompts

- `design-cartridge` — Guided workflow to create a cartridge from scratch
- `review-cartridge` — Automated play-test and bug report

### Quick Start for AI Agents

This is the typical MCP workflow for building and testing a cartridge:

```
1. wheriflo_init           → scaffold a new project
2. (edit the .lua file)    → write your cartridge logic
3. wheriflo_lint           → check for common mistakes
4. wheriflo_build          → compile to .gwc/.gwz + auto-starts web player
5. wheriflo_teleport       → enter a zone
   wheriflo_respond        → dismiss messages / answer prompts
   wheriflo_leave_zone     → test OnExit handlers
   wheriflo_get_state      → inspect zones, inventory, tasks
6. wheriflo_compile        → validate with official Groundspeak API
7. wheriflo_publish        → upload or update on wherigo.com
8. wheriflo_stop           → shut down the web player when done
```

Use `wheriflo_get_resource("api-reference")` to read the full Lua API
documentation, and `wheriflo_get_resource("design-guide")` for best practices.

---

### Setup: automatic (any client)

```bash
wheriflo setup
```

Detects installed AI clients and writes the MCP configuration for each.
The sections below are the equivalent manual steps, if you prefer to edit
configs yourself or need a workspace-scoped setup.

### Setup: VS Code / GitHub Copilot

Add to `.vscode/mcp.json` in your workspace:

```json
{
    "servers": {
        "wherigo": {
            "command": "uvx",
            "args": ["wheriflo", "mcp"]
        }
    }
}
```

Or open the Command Palette → `MCP: Open User Configuration` to add it globally.

If your agent environment restricts writes to the workspace, add workspace-local
uv directories so `uvx` does not try to use `~/.cache/uv` or
`~/.local/share/uv/tools`:

```json
{
    "servers": {
        "wherigo": {
            "command": "uvx",
            "args": ["wheriflo", "mcp"],
            "env": {
                "UV_CACHE_DIR": ".uv-cache",
                "UV_TOOL_DIR": ".uv-tools"
            }
        }
    }
}
```

### Setup: Claude Desktop

Add to your `claude_desktop_config.json`:

```json
{
    "mcpServers": {
        "wherigo": {
            "command": "uvx",
            "args": ["wheriflo", "mcp"]
        }
    }
}
```

On Windows, use:

```json
{
    "mcpServers": {
        "wherigo": {
            "command": "cmd",
            "args": ["/c", "uvx", "wheriflo", "mcp"]
        }
    }
}
```

### Setup: Claude Code

```bash
claude mcp add wherigo -- uvx wheriflo mcp
```

### Setup: OpenAI Codex

```bash
codex mcp add -- uvx wheriflo mcp
```

### Setup: Cursor

Add to Cursor Settings → MCP Servers:

```json
{
    "mcpServers": {
        "wherigo": {
            "command": "uvx",
            "args": ["wheriflo", "mcp"]
        }
    }
}
```

### Setup: pip install (alternative to uvx)

If you prefer a permanent install instead of `uvx`:

```bash
pip install wheriflo
```

Then replace `"command": "uvx", "args": ["wheriflo", "mcp"]` with
`"command": "wheriflo", "args": ["mcp"]` in any config above.

### Setup: Docker

```bash
docker build -t wheriflo .
```

```json
{
    "mcpServers": {
        "wherigo": {
            "command": "docker",
            "args": ["run", "--rm", "-i", "wheriflo"]
        }
    }
}
```

## Linting

Check a `.lua` file for common Wherigo mistakes without building:

```bash
wheriflo lint MyAdventure.lua
```

Checks for:
- `Media = nil` / `Icon = nil` (crashes iOS app)
- `ShowObjects = "OnEntry"` (wrong enum, should be `"OnEnter"`)
- `MoveObject()` usage (doesn't exist — suggests `MoveTo()` instead)
- Missing `require "Wherigo"`, `OnStart`, `OnEnter`, `OnGetInput`
- Unreachable zones (never set `Active = true`)
- Apostrophes in comments (crashes Groundspeak compiler)

Add `-- wheriflo:ignore` to any line to suppress warnings on that line.
For zones activated dynamically through helper functions, add the comment
to the `Wherigo.Zone()` constructor line.

Also available as the `wheriflo_lint` MCP tool.

## Debugging

Test the MCP server with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):

```bash
npx @modelcontextprotocol/inspector uvx wheriflo mcp
```

Or with `mcp dev`:

```bash
uv run mcp dev wheriflo/src/wheriflo/mcp_server.py
```

## Architecture

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'fontFamily': 'Segoe UI, sans-serif', 'lineColor': '#78909c'}}}%%
flowchart LR
    subgraph clients["Works with any MCP client"]
        direction TB
        VS["<b>VS Code</b>\n+ GitHub Copilot"]
        CL["<b>Claude</b>\nDesktop &amp; Code"]
        CX["<b>OpenAI Codex</b>"]
        CU["<b>Cursor</b>"]
    end

    subgraph mcp["wheriflo MCP Server"]
        direction TB
        tools["🛠️ <b>18 Tools</b>\ninit · build · compile · lint\nplay · teleport · leave_zone · set_position\nrespond · autoplay · get_state · get_resource\nedit · flow · publish · validate · reset · stop"]
        resources["📚 <b>3 Resources</b>\nAPI reference\nDesign guide\nStarter template"]
        prompts["💬 <b>2 Prompts</b>\ndesign-cartridge\nreview-cartridge"]
    end

    subgraph engine["Sandboxed Lua Engine"]
        direction TB
        shim["Wherigo API shim"]
        sandbox["🔒 No io · os · debug"]
        web["🌐 Web player\nlocalhost:8000"]
    end

    clients <-->|"stdio"| mcp
    mcp <--> engine

    style clients fill:#e3f2fd,stroke:#1a73e8,rx:12,color:#0d47a1
    style mcp fill:#fff3e0,stroke:#ff9800,rx:12,color:#e65100
    style engine fill:#fce4ec,stroke:#e91e63,rx:12,color:#880e4f
    style VS fill:#bbdefb,stroke:#42a5f5,rx:8,color:#0d47a1
    style CL fill:#bbdefb,stroke:#42a5f5,rx:8,color:#0d47a1
    style CX fill:#bbdefb,stroke:#42a5f5,rx:8,color:#0d47a1
    style CU fill:#bbdefb,stroke:#42a5f5,rx:8,color:#0d47a1
    style tools fill:#ffe0b2,stroke:#ffa726,rx:8,color:#e65100
    style resources fill:#ffe0b2,stroke:#ffa726,rx:8,color:#e65100
    style prompts fill:#ffe0b2,stroke:#ffa726,rx:8,color:#e65100
    style shim fill:#f8bbd0,stroke:#ec407a,rx:8,color:#880e4f
    style sandbox fill:#f8bbd0,stroke:#ec407a,rx:8,color:#880e4f
    style web fill:#f8bbd0,stroke:#ec407a,rx:8,color:#880e4f
```

## Troubleshooting

### `uvx` cache or tool directory permission errors

If a command fails with an error like `Failed to initialize cache at
~/.cache/uv`, `Could not create temporary file`, or a path under
`~/.local/share/uv/tools`, the failure is from `uvx` setup rather than
`wheriflo`, Groundspeak, or your cartridge. Point uv at writable directories:

```bash
UV_CACHE_DIR=.uv-cache UV_TOOL_DIR=.uv-tools uvx --from wheriflo wheriflo compile path/to/Cartridge.lua
```

For MCP server configs, put the same values in the server `env` block. Use
absolute paths if relative paths are resolved outside your project.

Run the diagnostic tool to check your setup:

```bash
wheriflo doctor
```

This verifies Python version, lupa, mcp SDK, credentials, Groundspeak API
reachability, and the starter template.

## Known Limitations

- **Media in web player** — images referenced in `ZMedia` objects are embedded in the GWC/GWZ and displayed in the web player during play-testing. Audio files are embedded but not played back in the web player.
- **Timers run in real time** — `ZTimer` Countdown and Interval timers fire `OnTick`/`OnStop` on real wall-clock schedules during play-testing. Because MCP play-testing is turn-based, a timer may fire between tool calls; check `wheriflo_get_state` after waiting out a timer. Timer state is not saved/restored.
- **No save/restore** — `OnRestore` is not implemented. The engine always starts fresh. Use `wheriflo_reset` to restart.
- **Single-player only** — the engine simulates one player. Multi-player or co-op cartridges can only be tested on the single-player path.
- **Limited GPS simulation** — zones are entered via `wheriflo_teleport`, not by simulating GPS movement. Use `wheriflo_set_position` to set arbitrary coordinates for proximity testing, but `OnDistanceChanged` callbacks don't fire automatically.
- **iOS line breaks** — only `[[ ]]` long strings render line breaks on the iOS Wherigo app. `\n` in quoted strings works on Android and the web player but not iOS. See the API reference for workarounds.
- **Web player is local only** — the browser player binds to `localhost` and is not suitable for remote access or sharing.

## Security

The Lua runtime is **sandboxed**: `io`, `os`, `debug`, `loadfile`, and `dofile`
are removed before any cartridge code executes. Cartridges can only use the
Wherigo API and standard Lua string/table/math operations.

See [SECURITY.md](SECURITY.md) for the full security policy and vulnerability reporting.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, project structure, and guidelines.

## License

MIT — see [LICENSE](LICENSE) for details.
