# 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; releases up to 0.16 also fell back to `~/.tea/tea.yml` when that file was missing). 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 mv ~/.tea/tea.yml ~/.tea/tea.yml.old # if you still use the oldest location ``` 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`. A `GITEA_TOKEN` without either was ignored; it now makes authenticated commands fail with exit 4, so set the instance or unset the token to use stored credentials | | `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 ``` It replaces every helper configured for the instance URL, the old entry included. With `--keep-helpers` it adds tea's helper after them instead; then remove the old entry with `git config --global --unset-all credential.https://gitea.example.org.helper '^!tea login helper$'`. `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 a usage error (`did you mean --remote origin?`) 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`, `rm`, `new`, `add`, `co`, `search issues`, `search prs`); `tea --help` lists them. `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` (value: see below) | | `actions variables set` | `variable set` (value: see below) | | `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` | Other long names changed too, mostly on `admin user`, `repo fork`, `repo edit` and `repo migrate`; an old name fails with an unknown-flag error, so check `--help` of each command you script. 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`), `-d` on `pr create` is `--draft` (was `--description`), `-b` on `pr create` is `--body` (was `--base`; use `-B`, otherwise the pull request silently targets the default branch), `-r` on `pr create` is `--reviewer` (was `--repo`; use `-R`) and `-o` on `secret` and `variable` is `--org` (was `--output`). 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` | | `secret set`, `variable set` | ` []`, `--file`, `--stdin` | value from `--body`, `--body-file FILE\|-` or piped standard input | 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. When stdout is not a terminal, lists print tab-separated values without header, color or links, with times in RFC 3339 UTC, and a `--page` hint goes to stderr; prefer `--json` in scripts. Commands that change resources report each change on stderr only on a terminal; warnings, such as a step that was skipped or failed, always go to stderr. Create commands print the URL of the new resource on stdout, so `url=$(tea issue create …)` works; other changes print nothing when piped. Bodies are rendered as Markdown only on a terminal (`tea config set markdown raw` turns this off). See `tea help formatting` for the terminal layout. ## 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 each shell's requirements. Packagers can ship the pre-generated scripts and man pages from the release docs archive, or run `make docs`. ## Behavior changes - Short flags changed meaning: `pr create -b` is now `--body` (it was `--base`, now `-B`), and `release edit --draft` / `--prerelease` are switches, so the 0.16 form `--draft false` is refused instead of being read as a tag; use `--draft=false`. - `search repo --private` and `--archived` are switches. Write `--private=false` where 0.16 accepted `--private false`. - `pr review reply ` takes the reply with `--body`, `--body-file` or `--editor`, not as a third argument. `repo create` names its template options `--template` and `--trust-model`; see `tea repo create --help`. - `--repo` takes `OWNER/REPO` or a URL; use `--cwd DIR` to point at a local checkout. - `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. - `label list` shows only the labels of the selected scope: the repository, or the organization with `--org ORG`. 0.16 also listed the organization labels of an organization-owned repository; `--exclude-org` was removed. - `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` exits with 4 when there is no account or a credential is missing or rejected. `auth token` prints the active account's token. - `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. - `--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` must find its workflow and check the inputs (without `--ref` it uses the checked-out branch, else the default 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.