{"id":51440656,"url":"https://github.com/olgasafonova/miro-cli","last_synced_at":"2026-07-05T11:01:46.250Z","repository":{"id":362778279,"uuid":"1234916057","full_name":"olgasafonova/miro-cli","owner":"olgasafonova","description":"Single-binary CLI that wraps the Miro REST API as shell commands, with a local SQLite mirror for offline search.","archived":false,"fork":false,"pushed_at":"2026-06-05T20:48:01.000Z","size":1010,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T22:21:08.370Z","etag":null,"topics":["claude","claude-code","cli","cobra","collaboration","go","golang","miro","miro-api","whiteboard"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/olgasafonova.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-10T20:02:18.000Z","updated_at":"2026-06-03T20:01:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/olgasafonova/miro-cli","commit_stats":null,"previous_names":["olgasafonova/miro-cli"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/olgasafonova/miro-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olgasafonova%2Fmiro-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olgasafonova%2Fmiro-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olgasafonova%2Fmiro-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olgasafonova%2Fmiro-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/olgasafonova","download_url":"https://codeload.github.com/olgasafonova/miro-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olgasafonova%2Fmiro-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35151638,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude","claude-code","cli","cobra","collaboration","go","golang","miro","miro-api","whiteboard"],"created_at":"2026-07-05T11:01:45.512Z","updated_at":"2026-07-05T11:01:46.240Z","avatar_url":"https://github.com/olgasafonova.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Miro Developer Platform CLI\n\nDrive Miro from your shell. Bulk-migrate stickies between boards, audit\nboard access, or script Miro operations in CI. One verb per Miro REST\nendpoint, JSON in and out, with a local SQLite mirror for offline search.\n\n## Why use it\n\n- **Shell-first.** Every endpoint is a flag-driven command. No hand-rolled\n  JSON envelopes, no pagination boilerplate, no curl + jq pipelines.\n- **Agent-friendly.** `--json`, `--dry-run`, `--select`, and `--agent` are\n  built in so the output is predictable for scripts and LLMs.\n- **Offline workflows.** `miro-cli sync` mirrors boards and items into a\n  local SQLite store; `miro-cli query` runs SQL and FTS5 search against it\n  without spending API quota.\n- **Bulk verbs.** `items bulk-create`, `items bulk-update`,\n  `items bulk-delete`, and `stickies create-grid` collapse N HTTP calls\n  into one for workshop and migration flows.\n- **Safe defaults.** Destructive verbs refuse to run without `--yes` (or\n  `--agent`, which implies it). `--idempotent` makes create retries safe.\n\n## When to reach for what\n\n| You want to... | Use |\n| --- | --- |\n| Script Miro from bash, CI, or a Makefile | `miro-cli` (this repo) |\n| Drive Miro from Claude Code as a skill | `miro-cli` + the bundled `SKILL.md` |\n| Drive Miro from a Claude Desktop / MCP-compatible agent | [miro-mcp-server](https://github.com/olgasafonova/miro-mcp-server) |\n| Render Miro data as interactive UI in chat (cards, tables, SVG graphs) | [miro-mcp-apps](https://github.com/olgasafonova/miro-mcp-apps) |\n| Embed Miro into a TypeScript or Python app | The official Miro SDKs |\n| Issue a one-off API call to test a payload | `miro-cli \u003cverb\u003e --dry-run`, or `curl` |\n\nThe CLI, the MCP server, and the MCP Apps server are complements, not\nalternatives: same author, overlapping API coverage, different runtimes.\nUse whichever ones fit. They all read the same `MIRO_ACCESS_TOKEN`.\n\n## Install\n\n### From source\n\n```bash\ngo install github.com/olgasafonova/miro-cli/cmd/miro-cli@latest\n```\n\nThis drops a `miro-cli` binary in `$GOPATH/bin` (typically `~/go/bin`). Make\nsure that directory is on your `PATH`.\n\n### Pre-built binary\n\nDownload the appropriate archive for your platform from the\n[latest release](https://github.com/olgasafonova/miro-cli/releases/latest)\nand extract the `miro-cli` binary into a directory on your `PATH`. On macOS,\nclear the Gatekeeper quarantine: `xattr -d com.apple.quarantine miro-cli`.\nOn Unix, mark it executable: `chmod +x miro-cli`.\n\n### Homebrew\n\n```bash\nbrew install olgasafonova/tap/miro-cli\n```\n\n## Quick Start\n\n### 1. Set your access token\n\nGet an access token from the\n[Miro developer portal](https://miro.com/app/settings/user-profile/apps) and\nexport it:\n\n```bash\nexport MIRO_ACCESS_TOKEN=\"your-token-here\"\n```\n\nOr pass `--token` on every invocation.\n\n### 2. Try your first command\n\n```bash\nmiro-cli boards list\nmiro-cli boards get --board-id \u003cid\u003e\nmiro-cli stickies create --board-id \u003cid\u003e --content \"Hello, Miro\" --color yellow\n```\n\nRun `miro-cli --help` for the full command reference, or\n`miro-cli \u003cgroup\u003e --help` for the verbs under any group.\n\n## Commands\n\nTwenty-two resource groups today. The tables below summarize each group at\na glance; run `miro-cli \u003cgroup\u003e --help` for the full verb list and flags.\n\n### Board management\n\n| Group | Purpose | Key verbs |\n| --- | --- | --- |\n| `boards` | Board lifecycle plus a few search / content helpers | `list`, `get`, `create`, `update`, `delete`, `copy`, `find`, `search`, `content`, `summary`, `picture`, `share`, `diagram`, `audit` |\n| `members` | Board access control | `list`, `get`, `update`, `remove` |\n\n### Item CRUD (one group per item type)\n\nEach group below ships `create` / `get` / `update` / `delete` against its\nown Miro REST resource. The table only calls out non-standard verbs.\n\n| Group | Resource path | Extras |\n| --- | --- | --- |\n| `stickies` | `/v2/boards/{id}/sticky_notes` | `create-grid` (bulk row-major layout) |\n| `shapes` | `/v2/boards/{id}/shapes` | `create-flowchart` (v2-experimental stencils) |\n| `texts` | `/v2/boards/{id}/texts` | — |\n| `frames` | `/v2/boards/{id}/frames` | — |\n| `cards` | `/v2/boards/{id}/cards` | — |\n| `app-cards` | `/v2/boards/{id}/app_cards` | — |\n| `connectors` | `/v2/boards/{id}/connectors` | `list` |\n| `embeds` | `/v2/boards/{id}/embeds` | — |\n| `documents` | `/v2/boards/{id}/documents` and `/v2/boards/{id}/docs` | `upload` (multipart from disk), `update-from-file` (replace bytes), `create-doc` (Markdown rich-text) |\n| `images` | `/v2/boards/{id}/images` | `upload`, `update-from-file` |\n| `codewidgets` | `/v2-experimental/boards/{id}/code_widgets` | read-only `list` (experimental) |\n| `mindmap` | `/v2-experimental/boards/{id}/mindmap_nodes` | `list` |\n| `tables` | `/v2/boards/{id}/data_table_formats` | read-only `list` and `get` |\n\n### Cross-type item operations\n\n`items` is the catch-all for verbs that don't care about the underlying\ntype. Bulk verbs are here because Miro's bulk endpoint accepts a typed\narray, not per-resource arrays.\n\n| Verb | What it does |\n| --- | --- |\n| `items list` / `items list-all` | Cursor-paginated list, or fetch everything until the cursor exhausts |\n| `items get` | Get any item by ID (returns the right shape based on `type`) |\n| `items update` / `items delete` | Cross-type partial update + delete |\n| `items bulk-create` | Create up to 20 items per call, mixed types |\n| `items bulk-update` | PATCH many items in one logical call (serial under the hood) |\n| `items bulk-delete` | DELETE many items in one logical call |\n| `items get-within-frame` | List items inside a frame |\n| `items get-by-tag` | List items carrying a tag |\n| `items get-tags` / `attach-tag` / `detach-tag` | Tag membership for a single item |\n\n### Tags and grouping\n\n| Group | Purpose | Verbs |\n| --- | --- | --- |\n| `tags` | Tag definitions on a board | `list`, `get`, `create`, `update`, `delete` |\n| `groups` | Group items so they move/resize as one | `list`, `get`, `get-items`, `create`, `update` (full PUT), `delete` (ungroup, items survive) |\n\n### Local store (offline workflow)\n\n| Command | What it does |\n| --- | --- |\n| `sync` | Mirror boards + items into a local SQLite store (incremental by default) |\n| `query` | Run a read-only SQL or FTS5 query against the store |\n\nSee the [Local Store](#local-store) section below for the full workflow.\n\n### Enterprise / audit\n\n| Group | Purpose | Notes |\n| --- | --- | --- |\n| `audit` | Enterprise audit log (`list-logs`) | Last 90 days; CSV export for older data |\n| `exports` | Org-scoped board export jobs (eDiscovery) | `create-job`, `list-job-tasks`, `get-job-status`, `get-job-results`, `get-task-link`, `update-job` |\n\nBoth require Enterprise-plan tokens with the matching scopes.\n\n## Output Formats\n\n```bash\n# Human-readable table (default in terminal, JSON when piped)\nmiro-cli boards get\n\n# JSON for scripting and agents\nmiro-cli boards get --json\n\n# Filter to specific fields\nmiro-cli boards get --json --select id,name,status\n\n# Dry run, show the request without sending\nmiro-cli boards get --dry-run\n\n# Agent mode, JSON + compact + no prompts in one flag\nmiro-cli boards get --agent\n```\n\n## Local Store\n\n`miro-cli` ships a local SQLite mirror of your boards and items so you can\nrun ad-hoc SQL and full-text search without burning API calls or waiting\nfor network round-trips. The default store path is\n`$XDG_DATA_HOME/miro-cli/store.db` (or `~/.local/share/miro-cli/store.db`);\noverride it for any command with `--store-path`.\n\n### `miro-cli sync`, populate the store\n\n```bash\n# Incremental: re-fetches a board's items only when modifiedAt has advanced\n# past the stored watermark. First run does a full sweep.\nmiro-cli sync\n\n# Force a full re-fetch (after a schema change, or when you suspect drift)\nmiro-cli sync --full\n\n# Pin the watermark for one run, useful when a previous sync was interrupted\nmiro-cli sync --since 2026-05-14T00:00:00Z\n```\n\nConflict policy is last-write-wins: the API is the source of truth, and\nrows are upserted by id. The watermark is stamped at the start of a\nsuccessful run, so a mid-run failure leaves the previous watermark in\nplace and the next run resumes.\n\n### `miro-cli query`, SQL passthrough\n\n```bash\n# Read-only SELECT against the store. Output is JSON when piped,\n# tab-separated table when stdout is a terminal.\nmiro-cli query \"SELECT id, name FROM boards ORDER BY modified_at DESC LIMIT 10\"\n\n# Items on a board, by type\nmiro-cli query \"SELECT type, COUNT(*) FROM items WHERE board_id = 'b1' GROUP BY type\"\n```\n\nThe connection is opened with `mode=ro` and `PRAGMA query_only=ON`;\nnon-SELECT input is rejected before execution. `--limit` (default `1000`)\ncaps the rows returned per query, pass `--limit 0` to disable the cap.\n\n### Full-text search\n\nAn `items_fts` FTS5 virtual table is kept in lockstep with `items` via\ntriggers. It indexes the textual fields Miro models as `data.content`,\n`data.title`, and `data.description`: sticky notes, cards, text widgets,\nshapes, app cards, and frame titles.\n\n```bash\n# Find every item mentioning \"roadmap\" across all synced boards\nmiro-cli query \"SELECT item_id, board_id, item_type FROM items_fts WHERE items_fts MATCH 'roadmap'\"\n\n# Phrase match, adjacent tokens in any indexed field\nmiro-cli query 'SELECT item_id FROM items_fts WHERE items_fts MATCH \"Q3 review\"'\n\n# Join back to items for richer columns\nmiro-cli query \"SELECT i.id, i.type, i.position_x, i.position_y\n  FROM items_fts f JOIN items i ON i.id = f.item_id\n  WHERE items_fts MATCH 'design'\"\n```\n\nThe tokenizer is `unicode61` (FTS5's default) and does not stem, so `fox`\nwill not match `foxes`. Use the `*` suffix for prefix matching: `MATCH 'fox*'`.\n\n## Agent Usage\n\nThis CLI is designed for AI agent consumption:\n\n- **Non-interactive.** Never prompts; every input is a flag.\n- **Pipeable.** `--json` writes structured output to stdout; errors go to stderr.\n- **Filterable.** `--select id,name,status` returns only the fields you need.\n- **Previewable.** `--dry-run` prints the request without sending it.\n- **Idempotent create.** `--idempotent` makes create-then-retry safe.\n- **Confirmable.** `--yes` (or `--agent`, which implies it) is required for destructive verbs.\n- **Offline-friendly.** `miro-cli sync` mirrors boards + items locally; `miro-cli query` runs SQL and FTS5 search against the mirror without API round-trips.\n- **Agent-safe by default.** No ANSI colors or formatting in output.\n\nExit codes: `0` success, `2` usage error, `3` not found, `4` auth error,\n`5` API error, `7` rate limited, `10` config error.\n\n## Use with Claude Code\n\nThis repo ships a `SKILL.md` that drives the CLI from Claude Code. Install\nthe binary first (see [Install](#install) above), then either drop\n`SKILL.md` into your project's `.claude/skills/miro-cli/` directory or load\nit via your skills-installer of choice. The skill verifies the binary is on\n`$PATH` before running any command.\n\nFor Miro MCP tools (board operations, stickies, frames, diagrams), use the\nseparate [miro-mcp-server](https://github.com/olgasafonova/miro-mcp-server)\nproject instead. This CLI is shell + skill only.\n\n## Configuration\n\n`miro-cli` reads credentials from two sources, in precedence order:\n\n1. `--token \u003cvalue\u003e` on any command\n2. `MIRO_ACCESS_TOKEN` environment variable\n\nA config file is not currently supported.\n\n## Troubleshooting\n\n**Authentication errors (exit code 4)**\n\n- Verify the environment variable is set: `echo $MIRO_ACCESS_TOKEN`\n- Try passing the token explicitly on one command: `miro-cli --token \u003cvalue\u003e boards list`\n- Generate a fresh token at https://miro.com/app/settings/user-profile/apps\n\n**Not found errors (exit code 3)**\n\n- Check the resource ID is correct\n- Run the `list` verb under the relevant group to see available items\n\n## Further reading\n\nMiro Developer Platform background, in case this is your first time touching\nthe REST API:\n\n- [Platform introduction](https://beta.developers.miro.com/docs/introduction)\n- [REST API quickstart (video)](https://beta.developers.miro.com/docs/try-out-the-rest-api-in-less-than-3-minutes)\n- [REST API quickstart (article)](https://beta.developers.miro.com/docs/build-your-first-hello-world-app-1)\n- [Getting started with OAuth 2.0](https://beta.developers.miro.com/docs/getting-started-with-oauth)\n- [Miro App Examples](https://github.com/miroapp/app-examples)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folgasafonova%2Fmiro-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folgasafonova%2Fmiro-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folgasafonova%2Fmiro-cli/lists"}