---
name: pp-inaturalist
description: "Explore iNaturalist's full API, plus privacy-safe field briefings and identification progress that ordinary endpoint wrappers miss. Trigger phrases: `what wildlife is nearby`, `make a nature scavenger hunt`, `did my iNaturalist observations get identified`, `compare wildlife seasons nearby`, `use iNaturalist`, `run iNaturalist`."
author: "avanderheyde"
license: "Apache-2.0"
argument-hint: "<command> [args] | install cli|mcp"
allowed-tools: "Read Bash"
metadata:
  openclaw:
    requires:
      bins:
        - inaturalist-pp-cli
    install:
      - kind: go
        bins: [inaturalist-pp-cli]
        module: github.com/mvanhorn/printing-press-library/library/other/inaturalist/cmd/inaturalist-pp-cli
---
<!-- GENERATED FILE — DO NOT EDIT.
     This file is a verbatim mirror of library/other/inaturalist/SKILL.md,
     regenerated post-merge by tools/generate-skills/. Hand-edits here are
     silently overwritten on the next regen. Edit the library/ source instead.
     See the repository agent guide, section "Generated artifacts: registry.json, cli-skills/". -->

# iNaturalist — Printing Press CLI

## Prerequisites: Install the CLI

This skill drives the `inaturalist-pp-cli` binary. **You must verify the CLI is installed before invoking any command from this skill.** If it is missing, install it first:

1. Install via the Printing Press installer. It defaults binaries to `$HOME/.local/bin` on macOS/Linux and `%LOCALAPPDATA%\Programs\PrintingPress\bin` on Windows:
   ```bash
   npx -y @mvanhorn/printing-press-library install inaturalist --cli-only
   ```
2. Verify: `inaturalist-pp-cli --version`
3. Ensure the reported install directory is on `$PATH` for the agent/runtime that will invoke this skill.

If the `npx` install fails (no Node, offline, etc.), fall back to a direct Go install (requires Go 1.26.5 or newer). This installs into `$GOPATH/bin` (default `$HOME/go/bin`), so add that directory to `$PATH` instead:

```bash
go install github.com/mvanhorn/printing-press-library/library/other/inaturalist/cmd/inaturalist-pp-cli@latest
```

If `--version` reports "command not found" after install, the runtime cannot see the binary directory on `$PATH`. Do not proceed with skill commands until verification succeeds.

Use nearby highlights and seasonal-shift for transparent, bounded biodiversity briefings. Create factual scavenger hunts from real taxa, and use identification commands to track whether observations gained community IDs without exposing locations.

## When to Use This CLI

Use this CLI for bounded iNaturalist discovery, taxon research, safe nearby field briefings, and observation-identification progress. Prefer it when raw endpoint data needs agent-shaped output or a local history, and use its exact privacy labels rather than trying to infer a more precise location.

## Anti-triggers

Do not use this CLI for:
- Do not use this CLI to recover, infer, or share obscured/private observation coordinates.
- Do not use this CLI for bulk data or media scraping; use iNaturalist exports or GBIF datasets.
- Do not use this CLI to create observations or other remote changes without explicit review and confirmation.

## Unique Capabilities

These capabilities aren't available in any other tool for this API.

### Privacy-safe field briefings
- **`nearby highlights`** — Get a transparent, recent wildlife briefing for an area without exposing observation coordinates.

  _Use this when an agent needs an explanation-backed local wildlife overview rather than a raw observation list._

  ```bash
  inaturalist-pp-cli nearby highlights --lat 37.7749 --lng -122.4194 --radius 5 --agent
  ```
- **`hunt create`** — Create a factual, balanced nature scavenger-hunt checklist from taxa actually observed nearby.

  _Use this to turn local biodiversity evidence into a safe field activity with traceable taxa._

  ```bash
  inaturalist-pp-cli hunt create --place-id 97394 --iconic-taxa Aves,Plantae --agent
  ```
- **`nearby seasonal-shift`** — Compare two field windows and surface taxa that newly appeared, returned, or changed materially.

  _Use this to answer how local wildlife changed between explicit time windows._

  ```bash
  inaturalist-pp-cli nearby seasonal-shift --place-id 97394 --recent-days 30 --baseline-days 30 --agent
  ```

### Identification progress
- **`observations id-status`** — See which of an observer's recent observations are identified, need IDs, disagree, or have no taxon.

  _Use this for a current identification-progress answer instead of manually filtering raw observations._

  ```bash
  inaturalist-pp-cli observations id-status --user inaturalist --since 30d --agent
  ```
- **`observations id-changes`** — Report observations that became identified, changed, withdrew, or still need IDs since a previous privacy-safe sync.

  _Use this to find identification progress since the last check without fabricating history._

  ```bash
  inaturalist-pp-cli observations id-changes --user inaturalist --since 30d --agent
  ```

## Command Reference

**annotations** — Create, delete, and vote

- `inaturalist-pp-cli annotations create` — Create an annotation
- `inaturalist-pp-cli annotations delete` — Delete an annotation

**colored-heatmap** — Manage colored heatmap


**comments** — Create, update, and delete

- `inaturalist-pp-cli comments create` — Create a comment
- `inaturalist-pp-cli comments delete` — Delete a comment
- `inaturalist-pp-cli comments update` — Update a comment

**controlled-terms** — Search and fetch

- `inaturalist-pp-cli controlled-terms list` — List all attribute controlled terms
- `inaturalist-pp-cli controlled-terms list-controlledterms` — Returns attribute controlled terms relevant to a taxon

**flags** — Create, update, and delete flags

- `inaturalist-pp-cli flags create` — Create a flag.
- `inaturalist-pp-cli flags delete` — Delete a flag
- `inaturalist-pp-cli flags update` — Update a flag. Generally only used to resolve the flag.

**grid** — Manage grid


**heatmap** — Manage heatmap


**identifications** — Create, update, and delete

- `inaturalist-pp-cli identifications create` — Create an identification
- `inaturalist-pp-cli identifications delete` — Delete an identification.
- `inaturalist-pp-cli identifications get` — Given an ID, or an array of IDs in comma-delimited format, returns corresponding identifications.
- `inaturalist-pp-cli identifications list` — Given zero to many of following parameters, returns identifications matching the search criteria
- `inaturalist-pp-cli identifications list-categories` — Given zero to many of following parameters
- `inaturalist-pp-cli identifications list-identifiers` — Given zero to many of following parameters
- `inaturalist-pp-cli identifications list-observers` — Given zero to many of following parameters
- `inaturalist-pp-cli identifications list-recenttaxa` — Returns an array of objects each containing an identification and a taxon.
- `inaturalist-pp-cli identifications list-similarspecies` — Returns species attached to IDs of observations of this taxon, or attached to observations identified as this species
- `inaturalist-pp-cli identifications list-speciescounts` — Given zero to many of following parameters
- `inaturalist-pp-cli identifications update` — Update an identification.

**messages** — Create, fetch, delete

- `inaturalist-pp-cli messages create` — Create and deliver a new message to another user
- `inaturalist-pp-cli messages delete` — This will all of the authenticated user's copies of the messages in tha thread to which the specified message belongs.
- `inaturalist-pp-cli messages get` — Retrieves all messages in the thread the specified message belongs to and marks them all as read.
- `inaturalist-pp-cli messages list` — Retrieve messages for the authenticated user. This does not mark them as read.
- `inaturalist-pp-cli messages list-unread` — Gets a count of messages the authenticated user has not read

**observation-field-values** — Create, update, and delete

- `inaturalist-pp-cli observation-field-values create` — Create an observation field value
- `inaturalist-pp-cli observation-field-values delete` — Delete an observation field value
- `inaturalist-pp-cli observation-field-values update` — Update an observation field value

**observation-photos** — Create and delete

- `inaturalist-pp-cli observation-photos create` — Create an observation photo
- `inaturalist-pp-cli observation-photos delete` — Delete an observation photo
- `inaturalist-pp-cli observation-photos update` — Update an observation photo

**observations** — CRUD, search, faving, quality metrics, stats, and more

- `inaturalist-pp-cli observations create` — Create an observation
- `inaturalist-pp-cli observations delete` — Delete an observation
- `inaturalist-pp-cli observations get` — Given an ID, or an array of IDs in comma-delimited format, returns corresponding observations.
- `inaturalist-pp-cli observations list` — Given zero to many of following parameters, returns observations matching the search criteria.
- `inaturalist-pp-cli observations list-deleted` — Given a starting date
- `inaturalist-pp-cli observations list-histogram` — Given zero to many of following parameters, returns histogram data about observations matching the search criteria
- `inaturalist-pp-cli observations list-identifiers` — Given zero to many of following parameters
- `inaturalist-pp-cli observations list-observers` — Given zero to many of following parameters
- `inaturalist-pp-cli observations list-popularfieldvalues` — Given zero to many of following parameters, returns an array of relevant controlled terms values and a monthly histogram
- `inaturalist-pp-cli observations list-speciescounts` — Given zero to many of following parameters
- `inaturalist-pp-cli observations list-updates` — Given zero to many of following parameters
- `inaturalist-pp-cli observations update` — Update an observation

**photos** — Manage photos

- `inaturalist-pp-cli photos` — Create a photo

**places** — Search and fetch

- `inaturalist-pp-cli places get` — Given an ID, or an array of IDs in comma-delimited format, returns corresponding places.
- `inaturalist-pp-cli places list` — Given an string, returns places with names starting with the search term.
- `inaturalist-pp-cli places list-nearby` — Given an bounding box, and an optional name query

**points** — Manage points


**posts** — Fetch site and project posts

- `inaturalist-pp-cli posts create` — Create a post
- `inaturalist-pp-cli posts delete` — Delete a post
- `inaturalist-pp-cli posts list` — Return journal posts from the iNaturalist site
- `inaturalist-pp-cli posts list-foruser` — Return journal posts from the iNaturalist site.
- `inaturalist-pp-cli posts update` — Update a post

**project-observations** — Create, update, and delete

- `inaturalist-pp-cli project-observations create` — Add an observation to a project
- `inaturalist-pp-cli project-observations delete` — Delete a project observation
- `inaturalist-pp-cli project-observations update` — Update a project observation

**projects** — Search and fetch projects and members

- `inaturalist-pp-cli projects get` — Given an ID, or an array of IDs in comma-delimited format, returns corresponding projects.
- `inaturalist-pp-cli projects list` — Given zero to many of following parameters, returns projects matching the search criteria
- `inaturalist-pp-cli projects list-autocomplete` — Given an string, returns projects with titles starting with the search term

**site_search** — Manage site search

- `inaturalist-pp-cli site-search` — Given zero to many of following parameters, returns object matching the search criteria

**subscriptions** — Manage subscriptions

- `inaturalist-pp-cli subscriptions create` — Toggles current user's subscription to this observation.
- `inaturalist-pp-cli subscriptions create-project` — Toggles current user's subscription to this project.

**taxa** — Search and fetch

- `inaturalist-pp-cli taxa get` — Given an ID, or an array of IDs in comma-delimited format, returns corresponding taxa.
- `inaturalist-pp-cli taxa list` — Given zero to many of following parameters, returns taxa matching the search criteria
- `inaturalist-pp-cli taxa list-autocomplete` — Given an string, returns taxa with names starting with the search term

**taxon-places** — Manage taxon places


**taxon-ranges** — Manage taxon ranges


**users** — Fetch and update

- `inaturalist-pp-cli users create` — Resend an email confirmation
- `inaturalist-pp-cli users get` — Given an ID, returns corresponding user
- `inaturalist-pp-cli users list` — Given an string, returns users with names or logins starting with the search term
- `inaturalist-pp-cli users list-me` — Fetch the logged-in user
- `inaturalist-pp-cli users update` — Update the logged-in user's session
- `inaturalist-pp-cli users update-id` — Update a user

**votes** — Manage votes

- `inaturalist-pp-cli votes create` — Vote on an annotation
- `inaturalist-pp-cli votes create-vote` — Vote on an observation. A vote with an empty `scope` is recorded as a `fave` of the observation.
- `inaturalist-pp-cli votes delete` — Remove a vote from annotation
- `inaturalist-pp-cli votes delete-unvote` — Remove a vote from an observation


### Finding the right command

When you know what you want to do but not which command does it, ask the CLI directly:

```bash
inaturalist-pp-cli which "<capability in your own words>"
```

`which` resolves a natural-language capability query to the best matching command from this CLI's curated feature index. Exit code `0` means at least one match; exit code `2` means no confident match — fall back to `--help` or use a narrower query.

## Recipes

### Nearby wildlife briefing

```bash
inaturalist-pp-cli nearby highlights --lat 37.7749 --lng -122.4194 --radius 5 --agent --select results.taxon_name,results.reason,results.geoprivacy
```

Return just the privacy-safe taxa, ranking rationale, and privacy state for an explicit area.

### Build a bird and plant hunt

```bash
inaturalist-pp-cli hunt create --place-id 97394 --iconic-taxa Aves,Plantae --agent
```

Make a factual checklist from observed taxa, without observation locations.

### Check identification progress

```bash
inaturalist-pp-cli observations id-status --user inaturalist --since 30d --agent
```

Group recent observations by their current identification state.

### Compare field windows

```bash
inaturalist-pp-cli nearby seasonal-shift --place-id 97394 --recent-days 30 --baseline-days 30 --agent
```

See transparent changes between two bounded observation windows.

## Auth Setup

Public read endpoints work without credentials. Authenticated iNaturalist responses can include private data, so the CLI must never surface or store private location fields in compound workflows; write commands require the official JWT/OAuth flow.

Run `inaturalist-pp-cli doctor` to verify setup.

## Agent Mode

Add `--agent` to any command. Expands to: `--json --compact --no-input --no-color --yes`.

- **Pipeable** — JSON on stdout, errors on stderr
- **Filterable** — `--select` keeps a subset of fields. Dotted paths descend into nested structures; arrays traverse element-wise. Critical for keeping context small on verbose APIs:

  ```bash
  inaturalist-pp-cli controlled-terms list --agent --select id,name,status
  ```
- **Previewable** — `--dry-run` shows the request without sending
- **Offline-friendly** — sync/search commands can use the local SQLite store when available
- **Non-interactive** — never prompts, every input is a flag
- **Explicit retries** — use `--idempotent` only when an already-existing create should count as success, and use `--ignore-missing` only when a missing delete target should count as success

### Response envelope

Commands that read from the local store or the API wrap output in a provenance envelope:

```json
{
  "meta": {"source": "live" | "local", "synced_at": "...", "reason": "..."},
  "results": <data>
}
```

Parse `.results` for data and `.meta.source` to know whether it's live or local. A human-readable `N results (live)` summary is printed to stderr only when stdout is a terminal AND no machine-format flag (`--json`, `--csv`, `--compact`, `--quiet`, `--plain`, `--select`) is set — piped/agent consumers and explicit-format runs get pure JSON on stdout.

## Paths and state

Agents should treat the CLI's path resolver as part of the runtime contract:

- Use `--home <dir>` for one invocation, or set `INATURALIST_HOME=<dir>` to relocate all four path kinds under one root.
- Use per-kind env vars only when a specific kind must diverge: `INATURALIST_CONFIG_DIR`, `INATURALIST_DATA_DIR`, `INATURALIST_STATE_DIR`, `INATURALIST_CACHE_DIR`.
- Resolution order is per-kind env var, `--home`, `INATURALIST_HOME`, XDG (`XDG_CONFIG_HOME`, `XDG_DATA_HOME`, `XDG_STATE_HOME`, `XDG_CACHE_HOME`), then platform defaults.
- `config` contains settings like `config.toml` and profiles. `data` contains `credentials.toml`, `data.db`, cookies, and auth sidecars. `state` contains persisted queries, jobs, and `teach.log`. `cache` contains regenerable HTTP/cache files.
- Stored secrets live in `credentials.toml` under the data dir. Existing legacy `config.toml` secrets are read for compatibility and leave `config.toml` on the first auth write.
- Run `inaturalist-pp-cli doctor --fail-on warn` to surface path and credential-location warnings. `agent-context` exposes a schema v4 `paths` block for agents that need the resolved dirs.
- For MCP, pass relocation through the MCP host config. The MCP binary does not inherit CLI flags:

  ```json
  {
    "mcpServers": {
      "inaturalist": {
        "command": "inaturalist-pp-mcp",
        "env": {
          "INATURALIST_HOME": "/srv/inaturalist"
        }
      }
    }
  }
  ```

Fleet precedence: an inherited per-kind env var overrides an explicit `--home` for that kind. Use `INATURALIST_HOME` or per-kind vars as durable fleet levers, and use `--home` only for a single invocation. Relocation is not reversible by unsetting env vars; move files manually before clearing `INATURALIST_HOME`, or `doctor` will not find credentials left under the former root.

## Automatic learning

This CLI ships a self-capturing learning loop. The CLI does its own bookkeeping: every invocation is journaled locally, a failed flag followed by a corrected retry auto-derives a `flag_alias` candidate, and a `teach` on a query family without a playbook auto-synthesizes a `playbook_candidate` from the session's journal. Your job is judgment only: `recall` first, act on surfaced candidates, `teach` the final answer, `playbook amend` when you observe a correction. You never record failures by hand.

### Step 1: `recall` before any discovery

Before list/search/drill commands on a new user question, run:

```bash
inaturalist-pp-cli recall "<user's question>" --agent
```

The response envelope:

```json
{
  "query": "...",
  "normalized": "<normalized form>",
  "query_entities": ["..."],
  "found": true | false,
  "match_score": 0.0,
  "results": [
    { "resource_id": "...", "resource_type": "...", "venue": "...",
      "confidence": 2, "entity_match": "exact|partial|unknown",
      "source": "taught|preseed|pattern", "warnings": ["..."] }
  ],
  "mismatches": [ /* only when --debug-mismatches */ ],
  "warnings": [ /* top-level */ ],
  "candidates": [
    { "id": 12, "class": "flag_alias | playbook_candidate",
      "summary": "...", "sightings": 3, "last_seen": "...",
      "rationale": "...",
      "next_action": ["<trial command>", "inaturalist-pp-cli learnings confirm 12"] }
  ],
  "playbook": {
    "query_family": "...",
    "playbook": {
      "steps": [ { "cmd": "<command with {slot} substitution>", "purpose": "..." } ],
      "entity_slots": ["$ENTITY"],
      "expected_tool_calls": 3
    },
    "slots_resolved": { "$ENTITY": { "token": "<live token>", "canonical": "<canonical>" } },
    "notes": "<workarounds + gotchas for thi