# Postede CLI command reference (preview)

**Developer preview — coming soon.** This work-in-progress reference is for
controlled testing with maintainer approval, an approved build and a registered
test environment. The CLI is not published on npm or ready for general users.
No API keys are offered. If test prerequisites are missing, stop before setup.

Package: `@postede/cli` (unpublished). Executable: `postede`. Node.js 24+.
See the [setup guide](https://postede.com/docs/cli/setup.md) for preview access.

Global options: `--env local|dev|production` (default production), `--json`,
`--help`, `--version`. Always select the intended environment explicitly.

| Command | Required input | Behavior |
| --- | --- | --- |
| `tools list` | None | List all 29 MCP-compatible operations and JSON input schemas offline |
| `tools call TOOL_NAME` | `--input JSON_FILE_OR_DASH` | Run an operation with the exact MCP snake_case arguments and results |
| `auth login` | Selected environment | Browser sign-in and read-only verification; `--no-browser` prints the sign-in URL |
| `auth status` | Selected environment | Verify the connection and read the current account |
| `auth logout` | Selected environment | Revoke refresh credential and remove local connection; `--local-only` removes local storage only |
| `projects list` | None | List accessible projects |
| `destinations list` | `--project PROJECT_ID` | List destinations and capabilities |
| `drafts list` | `--project PROJECT_ID` | List private text drafts; optional `--limit 1..50`, `--cursor CURSOR` |
| `drafts get POST_ID` | `--project PROJECT_ID` | Read the exact private draft and its current version |
| `drafts create` | `--project PROJECT_ID --destination DESTINATION_ID --file PATH` | Create a private text draft |
| `drafts update POST_ID` | `--project PROJECT_ID --expected-version VERSION --file PATH` | Edit that draft only if its version still matches |

## Read first

```sh
postede projects list --env local --json
postede destinations list --project PROJECT_ID --env local --json
postede drafts list --project PROJECT_ID --limit 10 --env local --json
postede drafts get POST_ID --project PROJECT_ID --env local --json
```

## Write only when requested

`--file` reads UTF-8 draft text. Use `--file -` for stdin. Do not infer IDs from
names or silently choose a project/destination. A create or update saves a
private draft; it does not approve, schedule, or publish it.

```sh
postede drafts create --project PROJECT_ID --destination DESTINATION_ID --file draft.txt --env local --json
postede drafts update POST_ID --project PROJECT_ID --expected-version VERSION --file draft.txt --env local --json
```

Writes accept `--request-id UUID_OR_ULID`. Preserve the same request ID and
payload when retrying an uncertain write; use a new ID for a new operation.
On a version conflict, fetch the current draft and review the change before
retrying with its version. Never overwrite another editor's changes blindly.

Use `--json` for structured automation output. Treat command errors as failures;
do not treat an unverified sign-in or failed write as success. The draft shortcuts do not schedule or publish. The tool interface exposes
scheduling and publishing as separate operations requiring their respective
OAuth permission and an explicit user request.


## Automation results

JSON success envelopes include `{projects}`, `{destinations}`,
`{drafts,nextCursor}`, `{post,requestId?}`, or `{environment,profile}`. Private
draft text is included in draft results; handle it as account data. Diagnostics
and errors go to stderr, including JSON objects when `--json` is selected.
Logout results include `revoked: true|false` unless `--local-only` is used. A
successful command with `revoked: false` does not prove server revocation.

| Exit code | Meaning |
| --- | --- |
| 0 | Success |
| 1 | Other API or CLI failure |
| 2 | Invalid usage or input |
| 3 | Login, credential storage, revocation, or connection lock issue |
| 4 | Permission denied |
| 5 | Network, timeout, rate limit, or server outage |
| 6 | Version, idempotency, or other API conflict |
| 130 | Interrupted command |

Logout revokes the refresh credential. For immediate full-grant revocation,
open `/agent/connections` in the same environment. `auth logout --local-only`
removes local credentials only and is not server revocation.

## Rate limits and retries

The CLI reuses a short-lived OAuth access token stored with its expiry in the
OS keychain for up to five minutes. It refreshes when less than 30 seconds
remain, under the same per-environment connection lock. Every authenticated
command still performs a fresh API token exchange that checks current consent;
API tokens stay in process memory. A revoked grant cannot use the cached token
to bypass that check.

Actual token refreshes have a burst cap of 20 requests; commands using the
cached OAuth token do not consume that refresh quota. The counter resets after
more than 60 seconds without an accepted token request. Spacing refreshes a
few seconds apart does not reset it.

On `RATE_LIMITED` (exit 5), stop issuing authenticated commands and wait for
the returned `retryAfterSeconds` before retrying. Other active clients can
share the limit, so a retry can still be rejected. Do not loop immediately,
reconnect repeatedly, or change account permissions to resolve a rate limit.
When retrying a write with an uncertain outcome, keep its original request ID
and payload so the API can recognize an exact replay.


## Full MCP tool parity

```sh
postede tools list --json
postede tools call list_projects --input projects.json --env local --json
```

For `list_projects`, projects.json contains `{}`. Use `--input -` for JSON on
stdin. `tools list` is offline and requires no account connection. It exposes
all 29 schemas so agents can inspect required fields before calling a tool.
`tools call` uses the same snake_case input and output contract as MCP, including
`project_id`, `expected_version`, and `request_id`. For tool writes, provide the
required `request_id` in the JSON; it is not generated by the tool interface.
Preserve that ID and the same payload when retrying an uncertain operation.

| Area | Tools |
| --- | --- |
| Account and projects | `get_profile`, `list_projects`, `list_publishing_destinations` |
| Private drafts | `list_drafts`, `get_post`, `create_draft`, `update_draft` |
| Delivery | `schedule_post`, `publish_post`, `list_scheduled_posts`, `cancel_scheduled_post`, `get_delivery_status` |
| Content types | `list_content_types`, `create_content_type`, `update_content_type`, `delete_content_type` |
| Rules | `list_rules`, `create_rule`, `update_rule`, `delete_rule` |
| Context | `get_project_context`, `list_context_sources`, `update_context_source` |
| Posting plan | `get_posting_plan`, `create_posting_window`, `update_posting_window`, `delete_posting_window` |
| Capabilities and calendar | `get_project_capabilities`, `list_calendar_entries` |

Scheduling and publishing are public actions. Only call them when the user
explicitly requests that exact action and destination. Setup never calls them.
Use the returned capability and connection status to determine what a
publishing destination currently supports. Do not infer support from a tool's
presence alone. Versioned configuration writes and deletes require the fields
specified by their schema. Full project parity on MCP is enabled by its
operator; CLI exposes those same contract operations.

## Tool permissions

The table below follows the executable CLI command definitions. Download the
[exact JSON input schemas](https://postede.com/docs/cli/tools.json), or inspect
the installed package with `postede tools list --json`.

| Tool | Required scope |
| --- | --- |
| `get_profile` | `postede.profile:read` |
| `list_projects` | `postede.projects:read` |
| `list_publishing_destinations` | `postede.projects:read` |
| `list_drafts` | `postede.drafts:read` |
| `get_post` | `postede.drafts:read` |
| `create_draft` | `postede.drafts:write` |
| `update_draft` | `postede.drafts:write` |
| `schedule_post` | `postede.posts:schedule` |
| `publish_post` | `postede.posts:publish` |
| `list_scheduled_posts` | `postede.posts:schedule` |
| `cancel_scheduled_post` | `postede.posts:schedule` |
| `get_delivery_status` | `postede.posts:publish` |
| `list_content_types` | `postede.configuration:read` |
| `create_content_type` | `postede.configuration:write` |
| `update_content_type` | `postede.configuration:write` |
| `delete_content_type` | `postede.configuration:write` |
| `list_rules` | `postede.configuration:read` |
| `create_rule` | `postede.configuration:write` |
| `update_rule` | `postede.configuration:write` |
| `delete_rule` | `postede.configuration:write` |
| `get_project_context` | `postede.configuration:read` |
| `list_context_sources` | `postede.configuration:read` |
| `get_posting_plan` | `postede.configuration:read` |
| `get_project_capabilities` | `postede.projects:read` |
| `list_calendar_entries` | `postede.calendar:read` |
| `create_posting_window` | `postede.posting-plan:write` |
| `update_posting_window` | `postede.posting-plan:write` |
| `delete_posting_window` | `postede.posting-plan:write` |
| `update_context_source` | `postede.configuration:write` |
