Metadata-Version: 2.5
Name: aegro
Version: 0.21.0
Summary: CLI for Aegro agricultural management API
Project-URL: Homepage, https://github.com/aegro/tool-aegro-cli
Project-URL: Repository, https://github.com/aegro/tool-aegro-cli.git
Project-URL: Issues, https://github.com/aegro/tool-aegro-cli/issues
Project-URL: Changelog, https://github.com/aegro/tool-aegro-cli/releases
Author-email: Aegro Engineering <pedro@aegro.com.br>
License-Expression: MIT
License-File: LICENSE
Keywords: agriculture,api-client,cli,farm-management,typer
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27.0
Requires-Dist: platformdirs<5.0.0,>=4.0.0
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: structlog>=24.0.0
Requires-Dist: truststore>=0.9.0
Requires-Dist: typer[all]>=0.12.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.34.0; extra == 'dev'
Requires-Dist: pytest<9.1,>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Description-Content-Type: text/markdown

# Aegro CLI

Command-line interface for the [Aegro](https://aegro.com.br) agricultural management API.

[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![PyPI](https://img.shields.io/pypi/v/aegro.svg)](https://pypi.org/project/aegro/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

---

## Install

```bash
# pipx (recommended)
pipx install aegro

# uv
uv tool install aegro

# Homebrew (macOS)
brew tap aegro/tap
brew install aegro
```

```powershell
# Windows — pipx (recommended)
pipx install aegro

# Windows — uv
uv tool install aegro

# Windows — pip
pip install aegro
```

> Credentials are stored in `%USERPROFILE%\.aegro\` (outside AppData on purpose: packaged apps like Claude Desktop and Store Python run under MSIX filesystem virtualization, which would silently redirect AppData writes to a per-app shadow folder). Files from the previous location (`%LOCALAPPDATA%\aegro\`) are migrated automatically on first run.

> **WSL**: Inside WSL, follow the Linux instructions. WSL and Windows credentials are stored separately — do not mix installations.

> **OneDrive/Dropbox**: Avoid pointing `AEGRO_CONFIG_DIR` to cloud-synced folders, as file locking may cause intermittent errors.

For development:

```bash
git clone https://github.com/aegro/tool-aegro-cli.git
cd tool-aegro-cli
uv sync
uv run aegro --help
```

---

## Quick Start

```bash
# 1. Authenticate — opens the browser for Aegro login (email/password, OTP or Google)
aegro auth login

# Alternative: static API key (CI, agents, fallback)
aegro auth login --farm-name "Fazenda Sul" --api-key "aegro_abc123..."

# 2. Select a farm
aegro farms list
aegro farms select "Fazenda Sul"       # persists to state.json (global per machine)

# Per-command farm — preferred with parallel sessions (one per farm), because
# one session's `farms select` retargets the others
aegro crops list --farm "Fazenda Sul"
aegro crops list --farm farm::abc123   # key: opaque, stable, no quoting needed

# 3. Start working
aegro crops list --start-date 2025-01-01 --end-date 2025-12-31
aegro financial installments --status NOT_PAID
aegro stock items --output table
```

---

## Commands

| Group | Commands | Domain |
|-------|----------|--------|
| `auth` | login, status, logout | Authentication |
| `farms` | list, select, info | Farm management |
| `files` | upload, attach, list-attachments (anexos: upload S3 + vinculo a realization, bill, purchase-order, purchase-requisition, harvest-log, asset, element, bank-transfer, livestock-lot; upload exige OAuth) | Attachments |
| `crops` | get, list, prorate, harvest-discounts, prorates, glebes | Harvest management |
| `activities` | get, list, plan, realizations, get-plan, get-realization, create-plan, create-realization (`--file` anexa), update-plan, update-realization (`--file` anexa), delete-activity, delete-realization | Activity planning |
| `financial` | bill, installment, installments, create-bill, update-bill, closes, realize, settle, category-usage, migrate-category (plan/apply/verify) | Accounts payable/receivable, incluindo troca de categoria em massa |
| `stock` | item, location, items, locations, logs, log, transfer, entry, removal | Inventory |
| `elements` | get, list, create-defensive/fertilizer/item/seed/service, set-categories | Inputs/supplies |
| `assets` | get, list, create-machine/vehicle/garner/immobilized/pivot/weather-station | Equipment |
| `fuel-supplies` | get, list, create, update | Fuel management |
| `maintenances` | get, list, create, update | Maintenance records |
| `harvest-logs` | get, create | Harvest records |
| `bank-accounts` | get, list, create | Bank accounts |
| `bank-reconciliation` | import-ofx, entries, candidates, confirm, ignore, undo, history, accounts, clear-pending | Bank reconciliation (OFX × financeiro; requires OAuth) |
| `companies` | get, list, create | Suppliers/vendors |
| `fin-categories` | get, list, create, subcategories | Chart of accounts |
| `catalogs` | list, element-keys, elements | Catalog lookups |
| `tags` | get, list, create | Tags/labels |
| `weather` | get, create | Weather data |
| `purchase-orders` | get, list, create | Purchase orders |
| `glebes` | get, list | Farm fields |
| `crop-glebes` | get, list | Crop fields |

---

## Output Formats

All commands support three output formats:

```bash
aegro crops list --output json    # JSON (default, for LLMs/scripts)
aegro crops list --output table   # Rich table for humans
aegro crops list --output csv     # CSV export
```

---

## Safe Mode for Mutations

Agents and automation should enable safe mode before running workflows that may
create, update, delete, realize, transfer, enter, or remove data:

```bash
export AEGRO_SAFE_MODE=1
aegro purchase-orders create ... --dry-run   # Preview request; no API mutation
# Execute after validating the payload. The farm must be explicit in safe mode
# (see below) — `--execute` on its own is refused.
aegro purchase-orders create ... --execute --farm "Fazenda Sul"
```

`--dry-run` prints the target farm (plus `farmSource`, where that farm came
from), method, endpoint, payload, and local warnings without calling the Aegro
API. With `AEGRO_SAFE_MODE=1`, mutating commands are blocked unless `--execute`
is passed. List/filter/get commands continue to work.

Safe mode also requires writes to name their farm explicitly. If the active farm
came from `state.json` (written by `farms select`, global per machine and shared
by every session), `--execute` fails with `IMPLICIT_FARM_BLOCKED` (exit 4):

```bash
export AEGRO_SAFE_MODE=1
aegro tags create --name Plantio --relation-type CROP --execute
# → IMPLICIT_FARM_BLOCKED: repeat with --farm "Fazenda Sul"
aegro tags create --name Plantio --relation-type CROP --execute --farm "Fazenda Sul"
```

This exists because parallel sessions — one per farm — used to overwrite each
other's `farms select`, silently retargeting writes at the wrong farm. `--farm`
is per-invocation, so it cannot race.

---

## Usage Examples

### Human workflow

```bash
# Morning overview
aegro farms select "Fazenda Norte"
aegro crops list --output table
# Date windows come in pairs (--due-date-start needs --due-date-end)
aegro financial installments --status NOT_PAID --due-date-start 2025-03-01 --due-date-end 2025-03-31 --output table
aegro stock items --output table

# Register a fuel supply
aegro fuel-supplies create --asset-key K --date 2025-03-13 --quantity 150 --unit L --cost 900

# Check harvest
aegro harvest-logs get <key>
```

### LLM/Agent workflow

```bash
# Agents use JSON output (default) for structured data
aegro crops list --start-date 2025-01-01 --end-date 2025-12-31
aegro activities list --crop-key <key>
aegro stock items
aegro financial installments --status NOT_PAID
```

---

## AI Skills

The source of truth for public Aegro skills is the Aegro-owned Claude plugin
repository [`aegro/skills`](https://github.com/aegro/skills). Internal
harness skills live separately in the private
[`aegro/skills-internal`](https://github.com/aegro/skills-internal) repository.

This CLI embeds a generated snapshot of `aegro/skills` in `aegro/_skills/`
for PyPI installs. Do not edit embedded skills directly; update `aegro/skills`
and then run:

```bash
python scripts/sync-skills.py <version>
```

```bash
/plugin marketplace add aegro/skills
/plugin install aegro-skills@aegro-skills
```

Users who install the CLI from PyPI get the skills with:

```bash
pip install --upgrade aegro
aegro skills install
```

`install` reads `aegro/skills` and falls back to the snapshot embedded in the
wheel only if the repository is unreachable. Pass `--offline` to require the
snapshot and skip the network entirely.

The snapshot is a safety net — corporate networks that intercept HTTPS, GitHub
being down, machines without access — not the source of truth. It is generated
at build time and deliberately **not** tracked in git; a checkout produces it
with `python scripts/sync-skills.py <version>`.

### Staying up to date

`aegro skills sync` is the explicit form: it always reads the repository, and
adds `--ref`, `--prune` and `--dry-run`. Unlike `install`, a network failure is
an error rather than a silent fallback.

```bash
aegro skills sync              # install/update from the default branch
aegro skills sync --dry-run    # show what would change
aegro skills sync --ref v0.7.0 # pin to a tag or commit
aegro skills sync --prune      # also drop skills deleted upstream
```

Sync overwrites local edits to `aegro-*` skills and repairs partially broken
ones — it makes the target directory match the repository. It records the
commit it installed in `.aegro-skills-sync.json`, which `aegro skills status`
reports as the provenance.

To keep an assistant current without anyone remembering to run it, wire it to a
`SessionStart` hook in `~/.claude/settings.json` (see
`docs/ev-cowork/settings.example.json`). Use `--quiet --offline-ok` there so a
session never fails or gets noisy because GitHub was unreachable:

```json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [{ "type": "command", "command": "aegro skills sync --quiet --offline-ok" }]
      }
    ]
  }
}
```

Overrides: `AEGRO_SKILLS_REPO`, `AEGRO_SKILLS_REF`, `AEGRO_SKILLS_PATH`.

### Behind a TLS-intercepting proxy

Corporate networks that inspect HTTPS re-sign traffic with their own root CA. The
OS trusts it, but Python verifies against the bundled `certifi` CAs, so `sync`
fails with `self-signed certificate in certificate chain`. `sync` retries with
the OS trust store automatically, which is usually enough. If it still fails,
point it at your network's root certificate:

```bash
export AEGRO_CA_BUNDLE=/path/to/corporate-root-ca.pem   # or SSL_CERT_FILE
```

`aegro skills install` needs no network at all and remains the offline fallback.
Never disable certificate verification to work around this.

Claude Code's own plugin marketplace (`autoUpdate`) does the same job natively
and is the better default when the assistant is Claude Code; `sync` covers other
tools and environments where that is not available.

### Domain skills (personas)

| Skill | Purpose |
|-------|---------|
| `aegro-agronomo` | Agronomic domain — crops, fields, activities, harvests, weather, inputs |
| `aegro-estoquista` | Stock domain — items, locations, movements, catalogs, elements |
| `aegro-financeiro` | Financial domain — bills, installments, categories, bank accounts, companies |
| `aegro-operacional` | Operational domain — farms, auth, tags, cross-domain orchestration |
| `aegro-patrimonial` | Asset domain — machines, vehicles, fuel supplies, maintenances |

### Workflow skills

| Skill | Purpose |
|-------|---------|
| `aegro-visao-geral` | Farm overview dashboard |
| `aegro-fechamento-safra` | Crop season closing checklist |
| `aegro-lancamento-financeiro` | Financial entry guide |
| `aegro-conciliacao-bancaria` | Bank reconciliation (OFX × financeiro) |
| `aegro-reconciliacao-estoque` | Stock reconciliation |
| `aegro-monitoramento-pragas` | Pest monitoring |
| `aegro-analise-rentabilidade` | Profitability analysis |
| `aegro-cadastro-patrimonio` | Asset registration |

---

## Configuration

### Authentication

Two authentication modes:

- **OAuth login (default)** — `aegro auth login` opens the browser for the
  regular Aegro login (email/password, e-mail code or Google). The CLI stores
  the user session (access + refresh token) and discovers the user's farms
  automatically. The session renews itself; no key handling needed.
- **API key** — `aegro auth login --api-key ...` stores a static per-farm key.
  Best for CI/CD, scripts and agents.

API key resolution priority (when both modes have credentials for a farm, the
API key wins):

1. `AEGRO_FARMS` env var (JSON) — for CI/CD and scripts
2. `AEGRO_FARMS_FILE` env var (path) — for Docker/K8s secrets
3. Config directory (`~/.config/aegro/` on Linux, `~/Library/Application Support/aegro/` on macOS, `%USERPROFILE%\.aegro\` on Windows) — interactive setup via `aegro auth login`

```bash
# Option 1: Browser login (recommended for humans)
aegro auth login

# Option 2: Environment variable
export AEGRO_FARMS='{"Fazenda Norte": "aegro_key1", "Fazenda Sul": "aegro_key2"}'

# Option 3: Static API key (saves to OS-native config directory)
aegro auth login --farm-name "Fazenda Norte" --api-key "aegro_key1"

# Inspect the session (method, user, expiry, farms)
aegro auth status

# Remove local credentials (revokes the OAuth session server-side)
aegro auth logout             # everything
aegro auth logout --env staging   # one environment only
```

### Active Farm Resolution

With credentials configured, the *active* farm is resolved from **two** sources,
in this order:

1. `--farm` flag — scoped to a single invocation, accepted by every API command
2. `state.json` written by `aegro farms select` — global per machine

**Use `--farm` when several sessions run in parallel, one per farm.** Only the
flag survives that: `state.json` is shared, so one session's `farms select`
retargets every other session.

`--farm` accepts either the farm **name** (case- and accent-insensitive, so
`"fazenda sul"` finds `"Fazenda Sul"`) or the farm **key** (`farm::...`), which
is opaque and stable — handy for avoiding shell quoting of names with spaces. An
ambiguous name is rejected rather than guessed.

```bash
# Scope a single command to one farm, regardless of what any session selected
aegro assets list --farm "Fazenda Norte"
aegro assets list --farm farm::abc123

# Confirm which farm is active, where it came from, and each farm's key
aegro farms list    # 'source': "flag" | "state" | null, plus 'key'
```

#### `AEGRO_ACTIVE_FARM` was removed

Three ways to name the same thing was itself the source of the confusion it was
meant to solve, so the env var is gone — `--farm` covers every case it did.

The removal is **loud, not silent**: while the variable is still set in the
environment, API commands fail with `ACTIVE_FARM_ENV_REMOVED` (exit 4) instead of
running. Ignoring it quietly would resolve the farm from `state.json` — a
*different* farm from the one the variable names — which is exactly the accident
`--farm` exists to prevent. Remove the variable and pass `--farm` instead.

### Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `AEGRO_API_BASE_URL` | `https://app.aegro.com.br` | Aegro API base URL |
| `AEGRO_FARMS` | — | JSON map of farm name → API key |
| `AEGRO_FARMS_FILE` | — | Path to JSON credentials file |
| `AEGRO_CONFIG_DIR` | — | Override config directory path |
| `AEGRO_SAFE_MODE` | — | Blocks mutating commands unless `--execute`; also requires the farm to come from `--farm`. Use `--dry-run` to preview |
| `AEGRO_OAUTH_CLIENT_ID` | `aegro-cli` | OAuth client id (`AEGRO_OAUTH_CLIENT_ID_<ENV>` for other envs) |
| `AEGRO_NO_VERSION_CHECK` | — | `1` disables the outdated-version notice (no PyPI lookup) |

### Outdated-version notice

`aegro --version` and `aegro auth status` compare the installed version against
PyPI and print one line to stderr when a newer release exists. Commands that fail
with `API_ERROR`, `UNEXPECTED` or `NOT_PERSISTED` carry the same hint as an
`upgrade` field next to `error` in the JSON on stderr — a contract error is very
often a CLI that predates the fix.

The lookup runs at most once a day (cached under the config dir; once an hour
after a failure), times out in 1.5s and fails silently: no network, no notice, no
change in behaviour. A successful command never triggers it. Set
`AEGRO_NO_VERSION_CHECK=1` to switch it off entirely.

### Updates are verified after they are written

`fuel-supplies update`, `maintenances update` and `harvest-logs update` re-read the
record after a successful PATCH and compare it against what was sent;
`purchase-orders create-shipment` re-reads the order and looks for the shipment the
POST returned. If the change did not persist, the command **fails** with
`NOT_PERSISTED` and exit code 1 instead of reporting success — an API that answers
200 and discards the payload is worse than one that errors, because the operator
marks the task done. What could not be compared with confidence (a unit the server
converted, a nested structure) is reported as unverified on stderr rather than
treated as a failure.

Pass `--skip-verify` to skip the re-read. On success the command prints the
re-read record; with `--skip-verify`, or if the re-read itself fails, it prints the
PATCH response instead.

---

## Development

```bash
# Install dependencies
uv sync

# Run tests
uv run pytest -v

# Lint
uv run ruff check aegro/ tests/

# Format
uv run ruff format aegro/ tests/
```

---

## Links

- [Aegro](https://aegro.com.br) — Agricultural management platform
- [Typer](https://typer.tiangolo.com/) — CLI framework
- [PyPI: aegro](https://pypi.org/project/aegro/) — Package page

---

## License

[MIT](LICENSE)
