---
name: pinokio
trigger: /pinokio
description: >
  Discover, launch, and drive Pinokio-managed local AI apps via the pterm
  CLI — ComfyUI, Fooocus, Stable Diffusion, FaceFusion, TTS engines, and any
  other Pinokio-packaged tool. Use ONLY when the user mentions Pinokio/pterm
  or asks to run a local AI app that Pinokio hosts. NOT for general CLI tools,
  web services, project code, or anything not installed through Pinokio.
user_invocable: true
---

> **Public / shared version** — trimmed from a private Claude Code setup and
> posted at smereski.com as a reusable pattern.

# Pinokio Runtime Skill

Use this skill for runtime control of Pinokio apps via `pterm`.
Do not ask users to manually install, launch, or call APIs when `pterm` can
do it. Not for general shell tools, project builds, or non-Pinokio services.

## Prerequisites

- **Pinokio** installed (your Pinokio installation directory).
- **pterm** CLI available — ships with Pinokio; may or may not be on `PATH`
  depending on how Pinokio was installed.

## Resolving `pterm`

If running outside Pinokio's own shell, `pterm` may not be on `PATH`.

1. Check if `pterm` works by name: run `pterm --version`.
2. If not on PATH, resolve via the Pinokio control plane:
   ```
   GET http://127.0.0.1:42000/pinokio/path/pterm
   ```
   Read `path` from the response. Normalize for your platform:
   - Windows: check for a sibling `.cmd` or `.ps1` shim.
3. If `127.0.0.1:42000` is blocked (EPERM/EACCES/sandbox):
   - This is a **permission problem**, not a missing runtime.
   - Request permission or report the sandbox block — do not conclude
     Pinokio is uninstalled.
4. If the control-plane probe fails, check common locations:
   - `which pterm` (macOS/Linux) or `where pterm` (Windows).
5. Only report "`pterm` unavailable" when both the command-form probe and
   the resolved/fallback path checks fail.

Use the working form consistently for all `pterm` commands in this skill.

## Core `pterm` commands

```bash
pterm search "<query>"                              # find installed apps
pterm status <app_id>                               # check app state
pterm run <app_path> [--default <selector>]...      # launch an app
pterm logs <app_id> --tail 200                      # tail launch logs
pterm which <command>                               # resolve a bundled binary
pterm stars                                         # list starred apps
pterm star <app_id>                                 # star an app
pterm unstar <app_id>                               # unstar an app
pterm registry search "<query>"                     # search installable apps
pterm download <uri> [name]                         # install from registry
pterm home                                          # print Pinokio home dir
```

Do not run update commands from this skill.

## Workflow: follow this order

### 1. Search for an installed app

Build a query from user intent — 2-4 high-signal capability tokens:
- Explicit name: `pterm search "ComfyUI"`
- By capability: `pterm search "tts speech synthesis"`

Run the search:
```bash
# 3+ term query
pterm search "<query>" --mode balanced --min-match 2 --limit 8

# 1-2 term query
pterm search "<query>" --mode balanced --min-match 1 --limit 8
```

If no useful hits, try a broad fallback:
```bash
pterm search "<query>" --mode broad --limit 8
```

**Ranking priority:**
1. Apps with `ready=true` (already running and reachable)
2. Apps with `running=true`
3. Offline apps that match the query

If the top candidate isn't clearly better than alternatives, show the user
the top 3 and ask to choose.

### 2. Registry fallback (only if local search found nothing)

```bash
# Ask the user first, then:
pterm registry search "<query>"
# After user selects one:
pterm download <uri>
# If "already exists" error:
pterm download <uri> <custom-local-name>
```

### 3. Run the app

```bash
# Check current state
pterm status <app_id>

# If offline or not ready, launch it
pterm run <app_path>
# If no explicit default menu item, infer selectors:
pterm run <app_path> --default 'run.js?mode=Default' --default run.js --default install.js

# Poll every 2s until ready (default timeout: 180s)
pterm status <app_id>
```

Status fields to watch:
- `running`: script is running
- `ready`: app is reachable
- `ready_url`: base URL for API calls
- `state`: `offline | starting | online`

Success: `state=online` and `ready=true`. Use `ready_url` as the API base.

Failure: timeout, or app drops back to `offline` after a run attempt.
On failure: `pterm logs <app_id> --tail 200` → return log tail + diagnosis.

**Retry cap:** at most 2 `pterm run` attempts per app. After the second
failure, stop, return logs + diagnosis, and hand back to the user.

### 4. API call strategy (once app is running)

Resolve where to write generated files before writing:
- Prefer the current working directory for task-scoped output.
- Use `pterm home` to get the Pinokio home dir for global storage.

File layout:
```
<working-dir>/pinokio_agent/clients/<app_id>/<operation>.<ext>   # generated client
<working-dir>/pinokio_agent/output/<app_id>/...                   # output files
```

First run for `<app_id>/<operation>`:
- Inspect app docs/code to infer endpoint + payload schema.
- Generate a minimal HTTP client file (JS/Python/shell).

Later runs: reuse the existing client file. Regenerate only if you hit a
404/405 (endpoint mismatch), 400/422 (payload mismatch), or auth error.

## Common app gotchas

- **Port conflicts:** apps have isolated Python/Node environments but bind to
  a fixed port. If another service is on the same port, pterm's `run` will
  fail or the app will silently not be `ready`. Check `pterm logs` for
  "address already in use."
- **Isolated environments:** each Pinokio app has its own Python/Node venv.
  Do not install packages into the system Python expecting them to be
  available inside the app. Use `pterm which <command>` to resolve the app's
  own binaries.
- **No endpoint docs:** some apps expose an HTTP API without published docs.
  Inspect the app's `run.js` or network requests from a running instance to
  discover the endpoint schema.
- **Dense output models (image gen):** inference can take 30-120 seconds.
  Poll via `pterm status` for `ready=true` before making the first API call;
  don't time out too early.
- **Registry downloads require disk space:** large AI models can be many GB.
  Confirm available space before `pterm download`.

## Behavior rules

- Only use `app_id`, `path`, and `ready_url` values echoed by actual `pterm`
  output — never fabricate or recall them from memory. If you don't have one,
  run the lookup again once, then stop and report.
- Do not hardcode app-specific endpoints when the user gave only a capability
  description (e.g., "run tts") — search first.
- Do not guess hidden endpoints; ask one targeted question if docs/code
  are unclear.
- Do not conflate loopback access failure or sandbox denial with "Pinokio
  is not running" or "`pterm` is not installed."
- Prefer returning full logs over brittle deterministic error parsing.

## Worked example A — capability only

User: "Generate TTS from this text: hello world"

1. `pterm search "tts speech synthesis" --mode balanced --min-match 2 --limit 8`
2. Found "Kokoro TTS" with `ready=false` → `pterm run <path>`
3. Poll `pterm status` until `ready=true`.
4. Generate a minimal Python client that POSTs to `ready_url + "/generate"`.
5. Return the output audio file path.

## Worked example B — no explicit default launcher

User: "Launch FaceFusion"

1. `pterm search "FaceFusion"` → found, offline.
2. `pterm run <path> --default 'run.js?mode=Default' --default run.js --default install.js`
3. Poll `pterm status` until ready.
4. Report `ready_url` and confirm the app is accessible.
