Skip to content

Firer/monday-cli

Repository files navigation

monday-cli

npm version CI Node License: MIT

An agent-first CLI for Monday.com. Pull tasks, file backlog items, transition statuses, and post comments from the terminal — designed for AI coding agents (Claude Code, Codex, Aider) with humans as a welcome second audience.


Why

AI coding agents need to operate on real tickets. Monday.com has a GraphQL API, but each agent learning that schema from scratch is wasteful — and the API is sharp-edged (40+ column types, idiosyncratic mutation shapes, complex pagination). monday-cli is the abstraction: one stable contract (universal envelope, 29 stable error codes, JSON Schema introspection) that every agent can target.

  • Agent-first ergonomics. --json everywhere, stable error.code, deterministic meta, no interactive prompts.
  • monday board describe emits paste-ready --set <token>=<value> examples for every writable column — agents discover board structure without reading external docs.
  • monday schema --json dumps every command's input flags and output shape as JSON Schema 2020-12 — no --help scraping.
  • --dry-run on every mutation; confirmation_required for destructive bulk ops (no surprise deletes).
  • Two-layer token redaction scrubs the API token from every emitted byte (logs, error messages, stack traces). Hardened against an adversarial fixture suite.

Install

npm install -g monday-cli

Requires Node.js ≥ 22.

Quick start

# 1. Set your Monday API token (admin or member; guests can't mint one).
#    Get one at https://<your-org>.monday.com/admin/integrations/api
export MONDAY_API_TOKEN="<your-token>"

# 2. Smoke test
monday account whoami --json

# 3. List a board's items (replace 12345 with your board ID)
monday item list --board 12345 --json

# 4. File a new task (v0.2)
monday item create --board 12345 --name "Refactor login" \
  --set status=Backlog --set 'Due date'=+1w --json

# 5. Find-or-create with idempotent matching (v0.2)
#    Re-running with the same args is safe — 0/1/2+ matches route to
#    create / update / `ambiguous_match` (the 27th stable error code).
monday item upsert --board 12345 --name "Refactor login" \
  --match-by name --set status='Working on it' --json

# 6. Move a ticket forward
monday item set 67890 status=Done --json

# 7. Comment on it
monday update create 67890 --body "Shipped in PR #1234" --json

Usage

The CLI follows a monday <noun> <verb> shape:

# Discovery
monday account whoami
monday board list
monday board describe <board-id>      # full board schema with column types

# Reading items
monday item list --board <board-id>
monday item list --board <board-id> --where status=Backlog --where owner=me
monday item list --board <board-id> --all --output ndjson | jq '...'
monday item get <item-id>
monday item find "Refactor login" --board <board-id>
monday item search --board <board-id> --where status=Done
monday item subitems <item-id>

# Updating items
monday item set <item-id> status=Done
monday item update <item-id> --set status=Done --set 'Due date'=+1w
monday item clear <item-id> status

# Comments (Monday "updates")
monday update list <item-id>
monday update create <item-id> --body "Shipped in PR #1234"

# Schemas (the agent's discovery hammer)
monday schema                          # full registry as JSON Schema 2020-12
monday schema item.set                 # one command's schema (dotted name)

# Diagnostics + escape hatch
monday board doctor <board-id>         # flag duplicate titles, non-writable
                                       # column types, broken board_relations
monday raw '{ me { id name email } }'  # GraphQL escape hatch
monday raw 'mutation { ... }' --allow-mutation --dry-run

For worked agent walkthroughs (pick up a backlog item → mark in-progress → leave a comment → mark done), filter DSL syntax, dry-run shapes, and error handling, see docs/examples.md.

Output format

  • TTY (you in a terminal): human-friendly tables, truncated to fit the terminal width.
  • Pipe / redirect: JSON, no flags needed — monday item list | jq works.
  • Agent in a pseudo-TTY: pass --json (alias for --output json) to force JSON regardless of terminal detection. JSON output is never truncated.

Every JSON response uses the same universal envelope:

{
  "ok": true,
  "data": ...,
  "meta": {
    "schema_version": "1",
    "api_version": "2026-01",
    "cli_version": "0.2.0",
    "request_id": "0e6f1a7b-...",
    "source": "live",
    "cache_age_seconds": null,
    "retrieved_at": "2026-05-01T10:00:00Z",
    "complexity": null
  },
  "warnings": []
}

Errors carry a stable error.code — agents key off the code, never the English message:

{
  "ok": false,
  "error": {
    "code": "rate_limited",
    "message": "...",
    "retryable": true,
    "retry_after_seconds": 30,
    "details": { "...": "..." }
  },
  "meta": { "..." }
}

The full envelope and error-code contract live in docs/cli-design.md §6 (binding) and docs/output-shapes.md (per-command reference).

Exit codes

Code Meaning
0 Success
1 Usage error (bad args, confirmation_required)
2 API or network error
3 Config error (missing token, etc.)
130 SIGINT (Ctrl-C)

Agent quickstart

If you're an AI coding agent driving this CLI:

  1. Always pass --json. Pseudo-TTY detection isn't reliable inside an agent harness. --json is an alias for --output json and forces JSON on every command. JSON is never truncated; tables are.
  2. Branch on error.code, not error.message. The 29 stable codes (not_found, confirmation_required, column_archived, unsupported_column_type, rate_limited, stale_cursor, ambiguous_match, tag_not_found, oauth_failed, …) are part of the contract. Messages are not.
  3. Read meta.source to know whether the data is "live" / "cache" / "mixed" / "none". "mixed" means board metadata came from cache while the rest hit live — non-trivial for writes because Monday's column state may have drifted. cache_age_seconds tells you how stale the cached portion is.
  4. Discover commands via monday schema --json. Every command's input flags + output data shape are introspectable as JSON Schema 2020-12 — no --help scraping.
  5. Discover board structure via monday board describe <board-id> --json. Each writable column carries example_set, paste-ready --set <token>=<value> strings the agent can use without external Monday docs.
  6. Use --dry-run on any mutation to preview the change as a planned_changes[] envelope before committing. Bulk ops without --yes return confirmation_required (exit 1) by default.
  7. Per-command output reference lives in docs/output-shapes.md — what data looks like for every shipped command. Worked agent sessions in docs/examples.md.

Configuration

The CLI reads configuration from environment variables. Source priority (first match wins):

  1. MONDAY_API_TOKEN in process.env (current shell).
  2. MONDAY_API_TOKEN=... in a .env file in the working directory.

--token <value> is not a supported flag — tokens passed on the command line leak via ps, shell history, and crash dumps. If you must pass one inline, prefer MONDAY_API_TOKEN=... monday ... so the token stays in the process env only.

The CLI sends Authorization: <token> (no Bearer prefix). Monday's API rejects the Bearer form.

See .env.example for all supported variables (API URL override, API-Version pin, request timeout, etc.).

Scope

v0.2.0 (published — first npm release as monday-cli): the v0.1 read-only core + safe-mutations surface PLUS the full mutation surface (item lifecycle, update mutations, workspace lifecycle, board lifecycle, board columns + groups). Built incrementally across M8–M18; one breaking change vs v0.1 (see CHANGELOG.md for the full upgrade guide). npm install -g monday-cli pulls the published artifact.

v0.1.0 (git tag, foundation milestone — not published to npm under the monday-cli name): read-only core (account, workspace, board, user, update, item) + safe mutations (item set / item clear / item update single + bulk, update create) + diagnostics (board doctor) + GraphQL escape hatch (raw) + filter DSL (--where + --filter-json) + cursor pagination with stale-cursor fail-fast + NDJSON streaming + local cache. v0.1.0 shipped to main as the foundation milestone but the npm publish slipped to v0.2.0; the v0.1 surface is fully present in the published v0.2.0 tarball.

What v0.2 added:

  • M8 added the --set-raw <col>=<json> escape hatch (bypasses the friendly translator; gated against read-only-forever and files-shaped types) and the link / email / phone firm-row friendly translators.
  • M9 added monday item create — top-level + classic-only subitem creation with single round-trip semantics, optional positional placement (--position before|after --relative-to <iid>), and the same --set / --set-raw surface as item update.
  • M10 closed the item-lifecycle cluster — monday item archive / delete / duplicate. The two destructive verbs share the --yes confirmation gate (--dry-run exempts) and read the source item for the dry-run preview; archive is wire-level idempotent, delete non-idempotent (re-running after an interim create would target the new item). duplicate is creative (no --yes), runs two-leg live (board lookup + mutation — Monday requires board_id), takes --with-updates to copy the source's comments, and extends the live envelope's data with duplicated_from_id so an agent has the source-ID echo handy.
  • M11 closed the four-verb lifecycle set with monday item move — same-board (--to-group <gid>) via move_item_to_group or cross-board (--to-group <gid> --to-board <bid>) via move_item_to_board. Cross-board moves use --columns-mapping '{<src>: <target>}' to bridge columns whose IDs differ between source and target; the strict default rejects unmatched columns pre-mutation with details.unmatched + details.example_mapping (agents copy-paste the seed into their retry) rather than letting Monday silently drop them. --columns-mapping {} is the explicit "drop everything (Monday's permissive default)" opt-in. --to-group is required for both forms because Monday's move_item_to_board(group_id: ID!) is mandatory. Value-overrides on cross-board mappings deferred to v0.3 (Monday's ColumnMappingInput carries no value slot — agents fire monday item set post-move when they need overrides).
  • M12 ships the idempotency cluster — monday item upsert
    • bulk monday item clear --where. Upsert takes --match-by <col>[,<col>...] plus --name / --set and routes 0 / 1 / 2+ matches to create_item / update_item / ambiguous_match (the 27th stable error code). Sequential- retry idempotent — re-running with the same args from the same agent is safe; concurrent agents are NOT a uniqueness guarantee (agents should pick a stable hidden key column for --match-by so race-induced duplicates surface as ambiguous_match on the next call). The match-by safe-list is intentionally narrow in v0.2: name / text / long_text / numbers / external_id-shaped hidden text round-trip verbatim; status / dropdown round-trip via label-text; people is restricted to me; date / link / email / phone are NOT v0.2-safe (the lookup-leg vs mutation-leg grammars don't reconverge at the wire — see cli-design §5.8 for the per-kind breakdown). Bulk clear --where extends M5b's per-item clear with the same cursor walk + --yes gate + per-item failure decoration as bulk update --where.

Writer allowlist (other types return unsupported_column_type with per-category guidance): status, text, long_text, numbers, dropdown, date, people, plus M8 firm row link, email, phone, plus v0.3-M19 row tags, board_relation, dependency.

  • M13 ships the full update mutation surface — monday update reply / edit / delete / like / unlike / pin / unpin / clear-all. The eight new verbs introduce the partial- success envelope (update clear-all returns ok: true with per-target outcomes in data.results: [...]); the envelope is ok: true whenever the dispatch ran, with per-target failures surfacing as data.results[i].error: { code, message } rather than top-level ok: false. M13 also flips update list's default-replies behaviour (now empty unless --with-replies is set — the one breaking change in v0.2).

  • M14 ships the workspace lifecycle — monday workspace create / update / delete / add-users / remove-users. add-users / remove-users reuse M13's partial-success envelope and add resolver-fronted dispatch (mixed numeric IDs

    • emails through userByEmail); per-token resolution failures land as records inside data.results with the input token echoed verbatim.
  • M15 ships the board lifecycle — monday board create / update / archive / delete / duplicate / add-users. board duplicate introduces the wrapped envelope shape (data: { board: <projection>, is_async }) because Monday's BoardDuplication carries an is_async slot the projection doesn't model. board update is per-attribute fan-out across Monday's update_board(board_attribute, new_value) surface with a force-live final read leg (Monday's per-attribute calls return only the changed slice).

  • M16 ships board column lifecycle + the §8 eager- invalidation contract — monday board column-create / column-update / column-delete. Every board-structure mutation calls invalidateBoard(boardId) post-success so a same-process board describe sees fresh state without TTL eviction. column-create adds the noncanonical_column_type warning for non-allowlisted column types with per-category suggested_write_path (raw_writable / read_only_forever / files_shaped). M16 retrofitted board update / archive / delete to participate in the §8 contract.

  • M17 ships board group lifecycle — monday board group- create / group-update / group-archive / group-duplicate / group-delete. Group-update is per-attribute fan-out across Monday's single update_group(group_attribute, new_value) surface with NO force-live read leg (Monday's update_group returns the full Group projection post-mutation, distinguishing group-update from board-update). Group-create + group-update validate --color against the pinned Monday-supported palette in src/api/group-color.ts. Group-archive carries a snapshot- bearing dry-run from cached board metadata; group-delete is destructive-no-read minimal.

  • M18 closed v0.2 with NDJSON streaming for item search + update list (the missing pair vs M7's item list streaming pin), envelope-snapshots refresh (60 → 92), output-shapes.md audit, README quickstart with item create + item upsert examples, this CHANGELOG, and the version bump to 0.2.0.

v0.3 in progress. M19 closedtags, board_relation, dependency friendly translators (slipped from v0.2 tentative at M18 close per cli-design §13 + §5.3) graduated; monday account tags read verb shipped (closes the §6.5 tag_not_found.details .hint forward-reference). M20 closed (documentation-only)monday item time-track start <iid> / ... stop <iid> are registered for forward-compatibility but reject today with usage_error: an empirical probe (2026-05-10, against API version 2026-01) confirmed Monday's public API does not currently support time-tracking column writes (change_simple_column_value

  • change_column_value both reject; the mutation root has zero time-tracking-related mutations). When Monday ships API support, the runtime swap is one-sided in src/api/time-tracking.ts — agent scripts targeting the verbs are stable across the swap. v0.3-M21 shipped (Part 1: 5c1f7ac + 81eec03 + a4cb5b0; Part 2: 1cd660a + 3b5cb30 + e21c166; close-docs 086be70 + coverage push 7058754): monday auth login --profile <name> + monday auth logout --profile <name> runtime bodies, the ~/.monday-cli/credentials mode-0600 cache, the ~/.monday-cli/config.toml TOML loader, the --profile <name> global flag's resolution wiring through cli/program.ts's preAction hook, and the redaction-runtime extension that folds credentials-cache tokens into the secret-bag for the §7.4.3 emission-path leak test. Pre-publish blocker (v0.3.0 release prep, after M28): swap the OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET placeholders in src/api/oauth.ts with the registered Monday OAuth app's values (one-time external step at https://developer.monday.com/apps with redirect URI exactly http://127.0.0.1:9876/callback). v0.3-M22 shipped (3a1b465, preceded by 84c6d2b redact narrowing): monday status runs the 7-probe matrix (DNS / TCP / TLS / auth / cache writability / redaction self-test / env-var pickup) per cli-design §11.5; monday usage reports the daily Monday API operation budget remaining from platform_api.daily_*. The empirical-probe finding pivoted the design at pre-flight — Monday tracks operations-per-day under platform_api.daily_*, NOT complexity points under a non-existent account.complexity — additive-only envelope per cli-design §11.5.3 Decision 8 closure. v0.3-M23 shipped (1f09a25 implementation, preceded by 1fefdb1 pre-flight contract diff + 9b93f15 Codex round-1 fixes + fa27b60 Codex round-2 fixes + 3a2f1db Decision 5 close): cross-board monday item search + monday board favorites runtime bodies live. Two new src/api modules (cross-board-search.ts per-board fan-out walker, board-favorites.ts 2-stage favorites resolver), one new command (monday board favorites), and an extension to monday item search for the v0.3 cross-board path (--workspace <wid> / --favorites / --max-boards <n> flags with mutual-exclusion). Decision 5 pinned --max-boards 25 default + hard cap 100, calibrated against wall-clock fan-out latency (NOT complexity-budget — empirical probe at 2026-05-11 measured ~25-30 complexity points per board against a ~999_950 per-call budget; latency at ~0.5-1.5s/N is the real constraint). board favorites is a 2-stage filter + hydrate against the polymorphic top-level Query.favorites: [GraphqlHierarchyObjectItem!] surface (Board | Folder | Dashboard | Workspace); the verb filters client-side to Board-typed entries + hydrates via boards(ids:) for the final row shape (id, name, state, workspace_id, url, position). Three load-bearing warnings on the cross-board path (inaccessible_boards, column_not_found_on_board, cross_board_truncated — v0.3 is single-call-only; no resumable cross-board cursor) + one on favorites (board_favorites_stale). Deferred to later v0.3 milestones: item history (M24), item update --continue-on-error partial-success (M25), monday dev workflow shortcuts (M26), notification send + webhook list/ create/delete (M27), multi-level subitem creation (M28). v0.4: monday item watch, --concurrency, asset uploads. See docs/cli-design.md §13 for the full roadmap, docs/v0.3-plan.md for the in-progress v0.3 milestones, and docs/v0.2-plan.md for the v0.2 milestone history.

See CHANGELOG.md for the per-release contract.

Documentation

Development

git clone https://github.com/Firer/monday-cli.git
cd monday-cli
npm install              # `prepare` hook auto-builds dist/
npm run dev -- account whoami --json    # tsx-based dev runner

# Quality gates (all must pass before merge):
npm run typecheck
npm run lint
npm test

The full dev workflow + how to add a new command is in docs/development.md. Conventions:

  • Strictest TypeScript (exactOptionalPropertyTypes, noUncheckedIndexedAccess, verbatimModuleSyntax).
  • No any (lint-enforced).
  • Parse at every boundary with zod.
  • Mock at the network boundary, not internal modules.
  • Branch coverage 94%+ floor.
  • Atomic commits, Conventional Commits.

Contributing

PRs welcome. Read docs/cli-design.md for the contract before writing code — anything that changes the output envelope or error codes is a major-version bump and requires explicit doc revision.

License

MIT © Nick Webster

About

A CLI tool for interracting with Monday.com - in particular for building AI agentic workflows that need access to read, add, update, re-structure etc anything that is in a Monday.com or Monday Dev board

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors