Appearance
CLI
The kendo CLI lets you manage your board from any terminal — create issues, check the board, log time — without switching to a browser.
Installation
Quick install (Linux / macOS)
bash
curl -sSL https://central.kendo.dev/cli/install.sh | shThe script detects your OS and architecture, downloads the correct binary, verifies checksums, and installs to ~/.local/bin/kendo.
Update
bash
kendo update # Download and install latest version
kendo update --check # Check for updates without installingVerify
bash
kendo versionGetting Started
bash
# 1. Log in (opens browser for authorization)
kendo auth login
# 2. Select your project
kendo project select KD
# 3. See your sprint board
kendo boardWorking across multiple tenants
The CLI can stay logged in to several tenants at once (for example script, emmie, and backtocode) and keeps each tenant's login and active project separate. You never hand-edit a config file or re-authenticate just to switch.
bash
kendo auth login --tenant emmie.kendo.dev # Add a tenant and make it active
kendo auth switch script # Switch the active tenant (no re-auth)
kendo auth status # List every tenant; the active one is markedauth switch and --tenant accept a full host (script.kendo.dev) or a unique subdomain (script).
Target one tenant for a single command
Use the global --tenant flag (or the KENDO_TENANT environment variable) to run one command against a specific tenant without changing the active one:
bash
kendo lane list --tenant emmie # Runs against emmie; active tenant unchanged
KENDO_TENANT=script kendo board # Same, via environment variablePrecedence is --tenant flag → KENDO_TENANT → the active tenant. Targeting a tenant you're not logged in to fails fast and names the tenant — it never silently runs against the wrong board. And when a --project code can't be found, the error names the tenant it searched, so a wrong-tenant mistake is easy to spot.
Logging out
bash
kendo auth logout # Log out of the active tenant
kendo auth logout --tenant emmie # Log out of a specific tenant
kendo auth logout --all # Log out of every tenantLogging out removes stored credentials but keeps the tenant remembered, so logging back in is quick. If you log out of the active tenant, another logged-in tenant is made active automatically and the CLI tells you which.
Upgrading from an older CLI
The first time you run a multi-tenant build after upgrading, any single-tenant login from an older CLI is reset — run kendo auth login once to sign in again.
Commands
Authentication
bash
kendo auth login # Log in (opens browser); becomes the active tenant
kendo auth login --tenant emmie.kendo.dev # Log in to a specific tenant
kendo auth switch script # Switch the active tenant (no re-auth)
kendo auth status # Show the active user and list all tenants
kendo auth status --json # Same, as JSON — includes your user id
kendo auth logout # Log out of the active tenant
kendo auth logout --all # Log out of every tenantSee Working across multiple tenants for the full model.
Users
bash
kendo user me # Your own record, including your numeric id
kendo user list # Every user you can see in the active tenant
kendo user list --json # Same, as JSONkendo search --assignee and the commands that set an assignee take a numeric user id, not an email or a name — for example kendo search --assignee 8. These two commands are how you find one. The issue filter flags are the exception: --assignee and --exclude-assignee on kendo issue list, issue search and issue count also accept a full name.
Ids are per-tenant
A user has a different id in every tenant they belong to; there is no id that spans tenants. Always resolve the id against the tenant you are about to query, which is what kendo user me --json reports alongside it:
bash
kendo search --assignee "$(kendo user me --json | jq -r .id)"kendo user list needs permission to read users. If your role doesn't have it the command says so and points you at kendo user me, which always works for your own id.
Projects
bash
kendo project list # Active projects
kendo project list --include-archived # Active and archived, with an ARCHIVED column
kendo project select KD # Set the active project
kendo project view # Show the active projectArchived projects leave project list and the project select picker, the same way they leave the sidebar in the web app. They stay fully usable by code: --project ATLAS resolves an archived project, and every command you run against it works normally.
Board & Sprint
bash
kendo board # Visual kanban board for active sprint
kendo board --rows 0 # Show all issues (no truncation)
kendo sprint active # Sprint name, dates, status
kendo sprint todo # To-do issues sorted by priorityIssues
bash
kendo issue list # All issues in active project
kendo issue list --sprint active # Filter by sprint
kendo issue list --label "bug" # Filter by label
kendo issue list --lane "To Do" --lane "In Progress" # Repeat a flag to match any of its values
kendo issue list --exclude-lane Done --exclude-type task # Leave values out
kendo issue list --assignee none # Unassigned issues
kendo issue search "login" --type bug --exclude-assignee none # Text search with the same filter flags
kendo issue count --group-by lane # Issues per lane, plus the total
kendo issue count --group-by assignee --sprint active --exclude-lane Done # Open sprint work per person
kendo issue view KD-0255 # View issue details
kendo issue my # Issues assigned to you (all projects)
kendo issue create --title "Title" # Create an issue
kendo issue create --title "T" --description "D" --label "bug" # Create with label
kendo issue move KD-0255 2 # Move to lane 2
kendo issue comment KD-0255 -m "..." # Add a comment
kendo issue comment KD-0255 -m "..." --reply-to 41 # Reply to comment 41
kendo issue link-branch KD-0255 # Link current git branch
kendo issue unlink-branch KD-0255 # Unlink it again (names candidates if several are linked)
kendo issue unlink-branch KD-0255 fix/typo # Unlink a specific branch
kendo issue unlink-branch KD-0255 main --repo acme/website # Disambiguate across repos
kendo issue label set KD-0255 bug,tech-debt # Set labels on an issue
kendo issue label set KD-0255 # Clear all labels
kendo issue attachments KD-0255 # List attachments on an issue
kendo issue attach KD-0255 ./diagram.png # Upload a file to an issue
kendo issue download KD-0255 7 # Download attachment 7 to ~/Downloads
kendo search "query" # Search across all projectsFiltering issues
kendo issue list, kendo issue search and kendo issue count share one set of filter flags. Each flag can be repeated, and an issue matches when it has any of the values given for that flag. Different flags narrow each other down.
| Flag | Accepts |
|---|---|
--lane | Lane name or id |
--sprint | Sprint id, active, or none for the backlog |
--type | feature, bug, task |
--priority | highest, high, medium, low, lowest |
--label | Label name or id |
--assignee | User id or full name, or none for unassigned |
--epic | Epic id or title, or none for issues without an epic |
Every flag has an --exclude- twin, such as --exclude-lane or --exclude-assignee, that leaves the matching issues out:
- Excluding a person, an epic or a sprint keeps the issues that have none. Add
noneto the exclusion to drop those too. - A value that is both included and excluded is excluded.
- A name that matches nothing stops the command before it fetches any issue. A name shared by two people or two epics stops it too, and the error lists the candidates with their ids.
kendo issue count --group-by <lane|assignee|sprint|epic|priority|type|label> prints one row per group, largest first, and the total. --json prints { total, groups: [{ key, label, count }] }. Grouped by label, an issue with two labels counts in both rows, so the rows can add up to more than the total.
Time Tracking
bash
kendo time log KD-0255 --minutes 60 # Log time on an issue
kendo time log KD-0255 --minutes 60 --started-at 2026-08-14T09:30:00+02:00 # With a precise start
kendo time list # This week's entries
kendo time list --from 2026-03-01 --to 2026-03-31 # Date range--started-at takes an RFC3339 timestamp and sets when the work began.
Include the UTC offset
A value without an offset is recorded as UTC, not your local time — --started-at 2026-08-14T09:30:00 logged from CEST becomes 11:30 local. Write +02:00 (or Z) explicitly. The confirmation line echoes the instant that was actually stored, so you can see immediately if it isn't what you meant.
Token Usage
bash
kendo tokens log KD-0255 --model claude-opus-5-5 --in 1200 --out 350 # Record a session's usage on an issue
kendo tokens log KD-0255 --model claude-opus-5-5 --in 1200 --out 350 \
--cache-read 40000 --cache-creation 6000 --id "$SESSION_ID:claude-opus-5-5" # With the cache split and an event idkendo tokens log records the token usage of one agent session for one model on an issue. Send one call per model.
--model,--inand--outare required.--incounts input tokens without cache reads or writes.--cache-readand--cache-creationare optional. Left out, kendo stores them as "not reported", not0.--idnames the session and model. A second call with the same id on the same issue replaces the stored totals, so a retry or a resumed session never counts twice.--occurred-attakes an RFC3339 timestamp and defaults to now.
A 403 means your role lacks Token Events: Create: ask an admin. kendo issue view shows the issue's total on its AI tokens: line. The Token Usage guide has a Claude Code hook that calls this command at the end of every session.
Labels
bash
kendo label list # List project labels with colors
kendo label create --name "bug" --color red # Create a label
kendo label update 1 --name "bugfix" # Rename a label
kendo label update 1 --color green # Recolor a label
kendo label delete 1 # Delete a labelEpics & Sprints
bash
kendo epic list # List epics with status, issue and Done when progress
kendo epic view 9 # Goal, Done when, What done looks like, assignee, status
kendo epic create --title "Checkout" --goal "Pay in one tap" --assignee "Jane Doe"
kendo epic update 9 --wish "The customer can pay [KD-11, KD-12]" --wish "The customer gets a receipt"
kendo epic update 9 --check "Payments land in the ledger" # Set What done looks like
kendo epic update 9 --wish "" # Clear Done when
kendo epic tick 9 21 # Tick wish 21 (untick undoes it)
kendo epic issues 9 # Issues in an epic
kendo epic comments 9 # Read the comments on an epic
kendo epic comment 9 -m "..." # Comment on an epic
kendo epic comment 9 -m "..." --reply-to 41 # Reply to comment 41
kendo epic attachments 9 # List attachments on an epic
kendo epic attach 9 ./roadmap.pdf # Upload a file to an epic
kendo epic download 9 7 # Download attachment 7
kendo sprint list # All sprints
kendo sprint list --json # Same, as JSON — status stays a bare intepic create and epic update take --goal, --assignee (a user id, a full name, or none), --color (grey, green, blue, red, purple, yellow, orange, teal, pink, indigo) and the two lists:
--wishsets Done when and--checksets What done looks like. Repeat the flag once per line, in order. The lines you pass replace the whole list.- A line with the same text as a stored one keeps it, so a kept wish keeps its tick and its issues.
- On
epic update, a wish links issues with a trailing bracket of keys or ids:--wish "Refunds work [KD-13]".[]unlinks them. A linked issue must belong to the epic, soepic createrefuses a bracket: attach the issues withkendo issue update <key> --epic <epic-id>first. --wish ""or--check ""alone clears that list. A flag you leave out leaves its list as it is.
epic update sends only the flags you pass. epic view prints each wish's id, which epic tick and epic untick take. epic comments, comment, update-comment and delete-comment work like their issue counterparts, with the epic id in place of the issue key; --reply-to works on issue comment too.
sprint list --json emits status as the same bare integer the table renders as a colour (0 Planned, 1 Active, 2 Completed) — the legend status: 0=Planned 1=Active 2=Completed prints to stderr on a terminal so scripts piping stdout stay unaffected, and it disappears with 2>/dev/null or when stdout is otherwise not a terminal.
Reports
bash
kendo report list # List all reports with attachment counts
kendo report view 5 # Full detail for one report, with its attachments
kendo report list --status pending # List only reports awaiting triage
kendo report list --status promoted # List only reports already promoted to issues
kendo report list --status dismissed # List only reports dismissed during triage
kendo report attachments 5 # List attachments on a report
kendo report attach 5 ./screenshot.png # Upload a file to a report
kendo report download 5 7 # Download attachment 7 to ~/Downloads--status accepts pending, promoted, or dismissed (case-insensitive). Unknown values fail fast with a clear error before hitting the API.
Attachments
Upload local files to any issue, report, or epic. The server accepts up to 20MB per file across common document, image, and media types.
bash
kendo issue attach KD-0255 ./diagram.png # Upload to an issue
kendo report attach 5 ./screenshot.png # Upload to a report
kendo epic attach 9 ./roadmap.pdf # Upload to an epic
kendo issue attach KD-0255 ./diagram.png --json # JSON output for scriptingOn success, a one-line confirmation is printed to stderr; with --json, the created attachment is emitted on stdout so the command is safely pipeable.
Migrations
bash
# One-time setup — get a Jira API token at id.atlassian.com/manage-profile/security/api-tokens
export JIRA_EMAIL=[email protected] JIRA_API_TOKEN=...
kendo migrate jira-attach KD-42 https://company.atlassian.net/rest/api/3/attachment/content/1001Streams a single attachment from a Jira content URL straight into a kendo issue — no temp file. Requires JIRA_EMAIL and JIRA_API_TOKEN in the environment. --json emits the created attachment on stdout so the command is scriptable inside a larger migration loop.
Feedback
bash
kendo feedback send --title "Love the CLI" --description "Smooth flows, great DX"Sends feedback (bug report, suggestion, or idea about Kendo itself) to the Kendo team. Works from any tenant.
JSON Output
Add --json to any command for machine-readable output:
bash
kendo board --json # Full board data
kendo issue view KD-0255 --json # Issue details as JSONCLI vs MCP
The CLI and MCP server are complementary:
- MCP is for your AI assistant — it connects Claude Code, Cursor, and Codex to your board
- CLI is for when you're the one driving — quick terminal commands for common board operations
Both talk to the same board through the same API.