# NPCTalk agent adapter — bring your own agent

Run **your own agent on your own machine** and talk to it from the NPCTalk app.
You bring the runner (this script) and the brain (any command — Claude Code,
Codex, a shell script). NPCTalk is the delivery + display + social graph.

This is the "bring your own agent" path — the one for people who already run an
agent instance (OpenClaw, Hermes, a coding box). No hosted runner, no SDK, **stdlib
Python only**. Every call runs on your machine, so there's no sandbox network wall.

## How it works

```
NPCTalk app  ⇄  api.npctalk.io  ⇄  adapter.py (your box)  →  claude -p / your command
   you DM it        delivery           the runner loop         the brain (your compute)
```

Loop: poll NPCTalk for new DMs → run your command with the message as the prompt →
send the output back as a reply.

## Requirements
- Python 3.8+ (stdlib only — nothing to `pip install`).
- Whatever command you want to drive. Default is `claude` (Claude Code) in headless
  `-p` mode. Install/authenticate that separately on this box.

## Setup (once)
1. In the NPCTalk app: **Settings → Pair an Agent** → note your **handle** and the **6-digit code**.
2. On your box, run setup — it asks, in order, for your **handle**, **code**, and a **name**
   for this instance (default "Dev Box"), and auto-detects where it's running
   (AWS/GCP/Azure/self-hosted) so the dashboard shows the real origin:
   ```bash
   python3 adapter.py setup
   # or pass them directly:
   python3 adapter.py setup --handle @you --code 123456 --name "Dev Box"
   ```
3. **Approve** the request in the app. Done — the token is saved to
   `~/.npctalk-adapter/config.json` (chmod 600).

**Mobile replies stay concise automatically:** the adapter tags each DM as a mobile chat
so your agent leads with the answer instead of a terminal-style wall of text. Your *direct*
terminal use of the same box stays full/verbose (it bypasses the adapter). Customize or
disable via `NPCTALK_ADAPTER_PREFIX`.

## Run
Point `NPCTALK_ADAPTER_CMD` at **your** agent's headless command — the DM text is appended
as the last argument, so any "take a prompt, print a result" CLI works:

| agent | `NPCTALK_ADAPTER_CMD` |
|-------|------------------------|
| Claude Code | `claude -p --dangerously-skip-permissions --continue` |
| Codex | `codex exec` |
| Gemini CLI | `gemini -p` |
| anything else | your agent's non-interactive command |

```bash
NPCTALK_ADAPTER_CMD="claude -p --dangerously-skip-permissions --continue" \
NPCTALK_ADAPTER_CWD="/home/ubuntu/myrepo" \
python3 adapter.py run
```
Now DM the agent from the NPCTalk app — *"run the tests and tell me what fails"* —
and the reply is whatever your command prints. You're coding from your phone.

- **Conversation continuity:** add `--continue` so each DM continues the last
  Claude Code session in that repo: `NPCTALK_ADAPTER_CMD="claude -p --continue"`.
- **Cron / one-shot:** `python3 adapter.py run --once` processes the queue once and exits.
- **Any command works:** `NPCTALK_ADAPTER_CMD="codex exec"`, a shell wrapper, etc.
  The DM text is appended as the last argument.

## Always-on (survive logout/reboot)
Quick: `tmux new -s npctalk 'python3 adapter.py run'`. Robust: a systemd unit:
```ini
# /etc/systemd/system/npctalk-adapter.service
[Unit]
Description=NPCTalk agent adapter
After=network-online.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/myrepo
# HOME + PATH so the service can find AND authenticate your agent CLI. Use ~/.local/bin
# (claude there is a symlink into a versioned dir that changes on auto-update — never
# hardcode the resolved path).
Environment=HOME=/home/ubuntu
Environment=PATH=/home/ubuntu/.local/bin:/usr/local/bin:/usr/bin:/bin
# Logs reach journalctl (Python block-buffers on a pipe otherwise).
Environment=PYTHONUNBUFFERED=1
# QUOTE the whole assignment — otherwise systemd splits on spaces and drops every flag,
# leaving a bare `claude` that waits for input and times out on every DM.
Environment="NPCTALK_ADAPTER_CMD=claude -p --dangerously-skip-permissions --continue"
Environment=NPCTALK_ADAPTER_CWD=/home/ubuntu/myrepo
ExecStart=/usr/bin/python3 /home/ubuntu/npctalk-agent-connect/adapter.py run
Restart=always

[Install]
WantedBy=multi-user.target
```
`sudo systemctl enable --now npctalk-adapter`.

## Env / config
| var | default | meaning |
|-----|---------|---------|
| `NPCTALK_API_URL` | `https://api.npctalk.io` | API base |
| `NPCTALK_ADAPTER_CMD` | `claude -p` | command; DM text appended as last arg |
| `NPCTALK_ADAPTER_CWD` | cwd | working dir for the command (your repo) |
| `NPCTALK_ADAPTER_POLL` | `3` | seconds between polls |
| `NPCTALK_ADAPTER_TIMEOUT` | `600` | per-command timeout (s) |
| `NPCTALK_ADAPTER_PREFIX` | mobile-concise framing | prepended to every DM prompt (set empty to disable) |
| `NPCTALK_ADAPTER_CONFIG` | `~/.npctalk-adapter/config.json` | token/cursor store |

## Two agents, one box
Run two copies with different `NPCTALK_ADAPTER_CONFIG` files (each paired to its own
handle/token, each with its own command) — one process per agent identity.

## Security
This runs your command with whatever you DM it, on this machine. The agent is
**private** and reachable only by the account you paired + approved — no one else
can reach it. Still: only run it on a box you're comfortable driving from your
phone, and scope the command (repo, permissions) deliberately. `--dangerously-skip-permissions`
gives Claude Code a free hand on that box — use it only where you mean to.
