# Migrating to tea 1.0 tea 1.0 rebuilds the CLI around explicit, singular `noun verb` commands modeled on GitHub's `gh` (including its verb aliases and short flags), a split configuration, and per-instance accounts. Old command names, plural groups and many old short flags are gone, and scripts need updating. This guide lists what changed and what to use instead. Quick checklist: 1. Make sure your instances run Gitea 28 or newer ([Server requirements](#server-requirements)). 2. Move the old configuration away, or point tea 1.0 at a new directory ([Configuration](#old-configuration-file)). 3. Log in again ([Logging in](#logging-in-again)). 4. Run `tea auth setup-git` to replace the old `!tea login helper` Git credential entries ([Git credential helper](#git-credential-helper)). 5. Regenerate shell completions ([Shell completion](#shell-completion)). 6. Update scripts: [global flags](#global-flags), [commands](#renamed-commands), [arguments](#changed-positional-arguments) and [output](#output-formats). Run `tea --help` for the full flag list of any command. ## Server requirements Requires Gitea 28 or newer. tea no longer probes `/api/v1/version` or `/swagger.v1.json` to adapt to older servers: on an older instance, commands that need a newer endpoint fail with the server's HTTP error instead of a "not supported" message. ## Configuration and login ### Old configuration file Older releases kept logins and preferences in one file, `config.yml` in the `tea` directory under the platform configuration directory (`~/.config/tea/config.yml` on Linux; very old releases used `~/.tea/tea.yml`). tea 1.0 stores preferences in `config.yml` and instances and accounts in `hosts.yml`, in the same directory by default; `tea config path` prints both paths. When it finds an old-format file it stops with: ```text legacy tea configuration: archive the old config.yml or select a separate TEA_CONFIG_DIR, then run tea auth login; existing files have not been changed ``` Nothing is imported automatically. Either archive the old file: ```sh mv ~/.config/tea/config.yml ~/.config/tea/config.yml.old # adjust the path on macOS and Windows ``` or keep it and give tea 1.0 its own directory with `TEA_CONFIG_DIR`. Do not run an old tea against a directory written by tea 1.0: it reads the new `config.yml` as an empty login list and overwrites it on its next save. `tea config list` shows effective preferences. Old settings map as follows: | Old `config.yml` | tea 1.0 | |---|---| | `preferences.editor: true` | `tea config set editor ""` (a command, not a switch) | | `preferences.flag_defaults.remote` | `tea config set remote ` | | login `default: true` | `tea config set default_host ` | | login `insecure`, `ssh_host` | `tea config set --host insecure true`, `... ssh_host ` | | login `ssh_key`, `ssh_agent`, `ssh_certificate_principal`, `ssh_key_agent_pub` | `tea config set --host --user ssh_key …` (also `ssh_agent`, `ssh_certificate_principal`, `ssh_key_fingerprint`) | ### Logging in again Accounts are identified by instance URL and username instead of a login name. `tea auth login --host ` replaces `tea login add`: | Old | tea 1.0 | |---|---| | `tea login add --url URL --token TOKEN` | `tea auth login --host URL --with-token < token-file` (the token is read from stdin) | | `tea login add --oauth` | `tea auth login --host URL --oauth` | | `tea login add --ssh-key PATH` / `-s` | `tea auth login --host URL --ssh-key PATH` | | `--ssh-agent-key` / `-a`, `--ssh-agent-principal` / `-c` | same long flags, no shorthands | | `--insecure` / `-i` | `--insecure` | | `--user`, `--password`, `--otp`, `--scopes` (create a token with basic auth) | removed: create a token in the web UI (Settings → Applications) or use `--oauth` | | `--name`, `--no-version-check` | removed | | `--redirect-url` | removed: OAuth always listens on a random loopback port | | `--git-credentials` / `--helper` / `-j` | `tea auth setup-git --host URL` | Token logins of the old release kept the token in plain text under `logins[].token` in the old `config.yml`, so you can pipe it straight into `tea auth login --with-token` instead of creating a new one. Credentials go to the operating-system keyring (service `tea-cli-v2`). Pass `--credential-store file` to store them in private files under the configuration directory instead. The old release's OAuth tokens (keyring service `tea-cli`, and `credentials.json` in the old configuration directory) are not read; delete them once you have logged in again. Managing accounts: | Old | tea 1.0 | |---|---| | `tea login list`, `tea login status`, `tea whoami` | `tea auth status` (`--verify` checks each credential) | | `tea login default NAME` | `tea config set default_host URL`, and `tea auth switch USER --host URL` for the active account | | `tea login edit` | `tea config set --host URL ` | | `tea login delete NAME`, `tea logout NAME` | `tea auth logout --host URL [--user USER]` | | `tea login oauth-refresh` | `tea auth refresh` | ### Environment variables | Variable | tea 1.0 | |---|---| | `GITEA_TOKEN`, `GITEA_INSTANCE_URL` | supported; `TEA_HOST` is an alternative to `GITEA_INSTANCE_URL` | | `GITEA_INSTANCE_INSECURE`, `GITEA_INSTANCE_SSH_HOST` | supported | | `GH_TOKEN` | no longer read: use `GITEA_TOKEN` | | `GITEA_SERVER_URL`, `GITEA_SERVER_TOKEN`, `GITEA_SERVER_USER`, `GITEA_SERVER_PASSWORD`, `GITEA_SERVER_OTP`, `GITEA_SCOPES` | removed with `tea login add` | New variables: `TEA_CONFIG_DIR`, `TEA_HOST`, `TEA_USER`, `TEA_REPO`, `TEA_EDITOR`, `TEA_PAGER` and `TEA_COLOR`; see `tea config --help`. Environment credentials are never written to disk. ### Git credential helper `tea login helper setup` and `tea login add --git-credentials` wrote two entries to `~/.gitconfig` for each instance: an empty `credential..helper` and `!tea login helper`. tea 1.0 has no `login` command, so Git now gets an error from that helper and falls back to prompting. Register the new helper: ```sh tea auth setup-git --host https://gitea.example.org ``` `tea auth setup-git` replaces every credential helper configured for the instance URL, the old `!tea login helper` entry included, so that other helpers neither answer for tea nor keep a copy of its token. Pass `--keep-helpers` to add tea's helper after the existing ones instead; then remove the old entry yourself with `git config --global --unset-all credential.https://gitea.example.org.helper '^!tea login helper$'`. `tea auth status` reports whether the helper is configured. `tea repo clone`, `tea pr checkout`, `tea pr clean`, `tea repo sync` and `tea pr create --agit` authenticate Git without any helper; `tea auth setup-git` is only needed for plain `git` commands. Add `--local` to configure only the current repository. ## Global flags | Old | tea 1.0 | |---|---| | `--repo` / `-r` | `--repo` / `-R` | | `--remote` / `-R` | `--remote` (no shorthand) | | `--login` / `-l` | `--host URL`, plus `--user NAME` when the instance has several accounts | | `--output` / `-o`, `--fields` / `-f` | `--json [fields]` and `--jq` / `-q ` | | `--debug` / `--vvv` | `--debug` | | `--confirm` / `-y`, `--force` / `-f` on destructive commands | `--yes` / `-y` | `-R` now means `--repo`: `tea issue list -R origin` fails with `invalid repository URL` instead of selecting the `origin` remote. Use `--remote origin`. New global flags: `--no-input` disables prompts, editors and browsers, `--yes` (`-y`) accepts confirmations, `--cwd` selects the directory used for repository discovery, and `--sudo USER` acts as another user (administrators only). ## Renamed commands All command groups are singular, and the old built-in aliases (`c`, `e`, `i`, `ms`, `lgtm`, …) are gone. Subcommands take gh's verb aliases instead: `ls` for `list`, `rm` for `delete`, `new` for `create`, `add` for `issue comment create`, `pr comment create`, `ssh-key create` and `time create`, `co` for `pr checkout`, and `search issues` / `search prs` for `search issue` / `search pr`. `tea --help` lists the aliases of each command. `tea alias set NAME EXPANSION` defines your own top-level shortcuts, for example `tea alias set prs 'pr list --author @me'`. Top-level groups: | Old | tea 1.0 | |---|---| | `issues`, `issue`, `i` | `issue` | | `pulls`, `pull`, `pr` | `pr` | | `labels` | `label` | | `milestones`, `ms` | `milestone` | | `releases`, `r` | `release` | | `times`, `t` | `time` | | `organizations`, `org` | `org` | | `repos` | `repo` | | `branches`, `b` | `branch` | | `actions secrets` | `secret` | | `actions variables` | `variable` | | `actions runs` | `run` | | `actions workflows` | `workflow` | | `webhooks`, `hooks` | `webhook` | | `comments`, `c` | `issue comment` / `pr comment` | | `notifications`, `n` | `notification` | | `ssh-keys` | `ssh-key` | | `admin users`, `admin u` | `admin user` | | `logins`, `login`, `logout`, `whoami` | `auth` (see [Logging in](#logging-in-again)) | | `clone`, `C` | `repo clone` | | `open`, `o` | removed (see [Removed features](#removed-features)) | Groups no longer run a default action: `tea issues` listed issues and `tea issues 5` showed issue 5; use `tea issue list` and `tea issue view 5`. The same applies to `pulls`, `notifications` and the other groups. Subcommands: | Old | tea 1.0 | |---|---| | `issues reopen`, `pulls reopen` (alias `open`) | `issue reopen`, `pr reopen` | | `pulls approve [comment]` | `pr review --approve [--body TEXT]` (`-a`) | | `pulls reject ` | `pr review --request-changes --body TEXT` (`-r`) | | `pulls review ` | `pr review ` (`--comment` / `-c` for a plain review comment) | | `pulls review-comments` | `pr review comments` | | `pulls reply`, `resolve`, `unresolve` | `pr review reply`, `pr review resolve`, `pr review unresolve` | | `labels update --id ID` | `label edit ` | | `labels delete --id ID` | `label delete ...` | | `labels list --save` | `label export` | | `milestones issues ` | `milestone issue list ` (title or ID) | | `milestones issues add\|remove` | `milestone issue add\|remove ...` | | `releases assets create` | `release asset upload` | | `times add` | `time create` | | `times list [user\|#issue]` | `time list [--username USER\|--issue N]` | | `repos search` | `search repo` | | `repos create-from-template` | `repo create --template OWNER/REPO` | | `actions secrets create` | `secret set` | | `actions variables list --name NAME` | `variable view NAME` | | `actions runs delete` (alias `cancel`) | `run delete`; `run cancel` cancels without deleting | | `actions workflows dispatch` | `workflow run` | | `webhooks update` | `webhook edit` | | `comments add [body]` | `issue comment --body TEXT` (also `pr comment`); `--edit-last` and `--delete-last` change your last comment | | `notifications read\|unread [all\|id]` | `notification mark-read\|mark-unread [id]...`; use `--all` for every filtered notification | | `ssh-keys add` | `ssh-key create` | | `login helper setup` | `auth setup-git` | | `completion pwsh` | `completion powershell` | Renamed flags that appear on many commands: | Old | tea 1.0 | |---|---| | `--description` / `-d`, `--description-file` (issues, pulls, comments) | `--body`, `--body-file` | | `--note` / `-n`, `--note-file` / `-f` (releases) | `--notes` / `-n`, `--notes-file` / `-F` (`--body` and `--body-file` are accepted too) | | `--content` / `-c` (wiki) | `--body`, `--body-file` | | `--labels` / `-L`, `--assignees` / `-a`, `--milestones` / `-m` | `--label` / `-l`, `--assignee` / `-a`, `--milestone` / `-m` | | `--referenced-version` / `-v` | `--ref` | | `--keyword` / `-k` | `--search` / `-S` | | `--from` / `-F` (issue list) | `--since` | | `--states` / `-s`, `--types` / `-t` (notifications) | `--status`, `--type` | | `--page` / `-p`, `--limit` / `--lm` | `--page`, `--limit` / `-L` | | `--style` / `-s` (pr merge) | `--merge`, `--squash` or `--rebase`, or `--method` / `-s`; without one, the repository's default merge style | | `--owner` / `-O`, `--init`, `--labels`, `--trustmodel`, `--branch`, `--template` (repo create) | `--org`, `--auto-init`, `--label-template`, `--trust-model`, `--default-branch`, `--as-template` | | `--inactive` (webhook) | `--active=false` | | `--out` (man) | `--output-file` / `-o` | The long names are unchanged unless listed here. The short flags follow gh instead of old tea: `-L` limit, `-s` state, `-l` label, `-a` assignee, `-A` author, `-m` milestone, `-t` title, `-b` body, `-F` body file, `-e` editor, `-w` web, `-S` search, plus the global `-R`, `-q` and `-y`. Some old shorthands now mean something else: `-L` is `--limit` (was `--labels`), and `-d` on `pr create` and `pr list` is `--draft` (was `--description`). Two flags kept their name but changed meaning: - `workflow run`: `-f` was `--follow` and now means `--field KEY=VALUE`; `--follow` has no shorthand. `-r` (`--ref`) and `-i` (`--input`) are unchanged. - `api`: `--field` (`-f`) sent a string and `--Field` (`-F`) a typed value. Now `--raw-field` / `-f` sends a string and `--field` / `-F` a typed value, so the shorthands behave as before. `--data` / `-d` became `--input FILE` (or `-` for stdin), and `--output` / `-o` became `--output-file`. ## Changed positional arguments | Command | Old | tea 1.0 | |---|---|---| | `milestone close\|reopen\|delete\|edit\|view` | milestone name | milestone title or ID | | `milestone issue list\|add\|remove` | milestone name | milestone title or ID | | `release asset delete ` | attachment name | asset name or ID | | `label edit\|delete` | `--id ID` | label name or ID | | `team view\|edit\|delete`, `team member\|repo ...` | team ID | `ORG/TEAM` or team ID | | `repo delete` | `--name NAME --owner OWNER` | optional `OWNER/REPO` argument (or `--repo`) | | `issue comment` | optional body argument | `--body`, `--body-file` or `--editor` | A number is read as an ID; pass `--by-name` to match a milestone, label or asset whose name is a number. Destructive commands still ask for confirmation unless `--yes` is given. ## Output formats `--output` / `-o` (`table`, `simple`, `csv`, `tsv`, `yaml`, `json`) and `--fields` are gone. Human output is text for people; scripts should use JSON: ```sh # old: tea issues list --fields index,title -o csv tea issue list --json number,title --jq '.[] | "\(.number),\(.title)"' # old: tea notifications --mine -o simple | xargs notify-send tea notification list -q '.[].title' | xargs -n1 notify-send ``` `--json` alone prints every field; `--json a,b` selects fields and rejects unknown names; `--jq` / `-q` filters the result and implies `--json`. Commands that produce no resource, such as deletes, reject `--json` with a usage error instead of printing nothing. See `tea help formatting`. Lists follow gh. On a terminal they print a "Showing 30 of 112 open issues in alice/project" line, an uppercase header row and columns fitted to the terminal width, with times relative ("about 3 hours ago"), states colored and the first column linked to the web page (`tea config set hyperlinks never` turns the links off). An empty list prints a message on stderr and exits 0. When stdout is not a terminal, lists print tab-separated values without header, color or links, with times in RFC 3339 UTC and stable columns, so `cut -f1` works; a `--page` hint goes to stderr when more results follow. Still prefer `--json` in scripts: the columns may change. Tabs and line breaks in list cells become spaces, and control characters such as escape sequences are dropped from all human output, bodies included. Commands that change resources report each change on stderr, such as `✓ Closed issue alice/project#5 (Fix login)` or `! … is already closed`, only when stdout is a terminal. Create commands print the URL of the new resource on stdout, so `url=$(tea issue create …)` works; other changes print nothing when piped. With `--json` they print the changed resource instead. Issue, pull request, release and comment bodies are rendered as Markdown on a terminal and printed as written when piped; `tea config set markdown raw` always prints them as written. Long output on a terminal goes through a pager (`TEA_PAGER`, the `pager` preference or `PAGER`). Each command starts the pager at most once. ## Exit codes | Code | Meaning | |---|---| | 0 | success, including an empty list | | 1 | the command failed | | 2 | usage error: an unknown command or flag, or a missing or invalid argument | | 3 | canceled: a declined confirmation, an aborted prompt or a timeout | | 4 | authentication is missing or was rejected | | 8 | `tea pr checks`: some checks are still pending | | 130 | interrupted with Ctrl-C | `tea help exit-codes` prints this table. ## Removed features | Feature | Replacement | |---|---| | `tea open [number\|issues\|pulls\|releases\|commits\|branches\|wiki\|labels\|milestones\|activity\|settings\|]` | `tea issue view N --web`, `tea pr view N --web`, `tea repo view --web`; for other pages open `$(tea repo view --json url --jq .url)/releases` and similar | | Basic-auth token creation (`login add --user/--password/--otp/--scopes`) | create a token in the web UI, or `tea auth login --oauth` | | `login edit` | `tea config set --host URL ` | | Output formats and `--fields` | `--json` and `--jq` | | `contrib/autocomplete.*` scripts and the `--generate-shell-completion` protocol | `tea completion ` | | `GH_TOKEN`, `GITEA_SERVER_*`, `GITEA_SCOPES` | `GITEA_TOKEN`, `tea auth login` | | `--vvv` | `--debug` | ### Shell completion Completion scripts saved from an older `tea completion` (or the removed `contrib/autocomplete.*` files) call tea with `--generate-shell-completion`, which tea 1.0 rejects, so completion silently stops working. Regenerate them: ```sh eval "$(tea completion bash)" # ~/.bashrc autoload -U compinit && compinit; source <(tea completion zsh) # ~/.zshrc tea completion fish > ~/.config/fish/completions/tea.fish tea completion powershell | Out-String | Invoke-Expression # PowerShell profile ``` `tea completion --help` explains the requirements of each shell and how to install the script system-wide. Packagers can ship the pre-generated scripts and man pages from the release docs archive, or run `make docs`. ## Behavior changes - `pr checkout N` (alias `co`) checks out a local branch named after the PR's head branch when that name is free or already points at the head; otherwise it uses `OWNER/BRANCH` for a fork or `pr/N`. The branch tracks the head branch, so `git push` updates the pull request. Use `--branch NAME` (`-b`) to choose the name, `--update` to fast-forward an existing branch, or `--detach` for a detached checkout. - `pr review` takes the review type as `--approve` / `-a`, `--request-changes` / `-r` or `--comment` / `-c`; on a terminal, leaving it out starts an interactive review. - `notification` commands act on your whole inbox by default (the old `--mine`). `-R OWNER/REPO` or `--this-repo` limits them to one repository. - `config get KEY` prints only the value; `--show-source` adds where it comes from. - `auth status` groups accounts by instance, marks each as usable or not, and reports whether the Git credential helper is configured. It exits with 4 when there is no account or a credential is missing or rejected. `auth token` prints the token of the active account for scripts. - `webhook edit` must be told what to do with the delivery authorization header on every call, because Gitea replaces it: pass `--authorization-header`, `--authorization-header-file` or `--clear-authorization`. The signing secret cannot be edited; recreate the hook to rotate it. - `notification mark-read`, `mark-unread`, `pin` and `unpin` act on the given IDs; pass `--all` to apply them to every notification matching the filters. - `--no-input` disables prompts, editors and browsers. With `--json`, tea never prompts unless `--interactive` is given; required values must come from flags. - Destructive commands and `pr merge` confirm on a terminal and need `--yes` (`-y`) elsewhere; the help of each such command lists `--yes`. - `--limit` (`-L`) accepts 1 to 1000 and `--page` a positive number; other values are usage errors instead of being passed to the server. tea pages through the server's smaller pages to reach the limit. - In `--json` output, timestamps the server leaves unset print as `""`, or as `null` for optional fields such as deadlines, instead of `0001-01-01T00:00:00Z`. - More input is checked before anything is sent: release titles, issue titles and comment bodies cannot be empty, a review needs a body or inline comments (unless it approves), team creation needs a permission, migrations from GitHub, Gitea, GitLab and Gogs need an access token, and `workflow run` needs a ref (`--ref` or a checked-out branch). - `pr merge` merges the head it read and refuses when the branch moved in the meantime; run it again to merge the new head. - Preferences follow the git/gh order: an explicit `tea config set` value wins over `VISUAL`/`EDITOR`, `PAGER` and `BROWSER`, which win over the defaults; `TEA_EDITOR`, `TEA_PAGER` and `TEA_COLOR` override both.