## Why
Today `tea comment` can only *add* a comment. Editing or deleting requires falling back to `tea api`. This came up while I was iterating on PRs in this same repo earlier today and had to correct a couple of comments by hand. Every comparable forge CLI (gh, glab, etc.) exposes these operations as first-class commands.
## What
Restructures `tea comment` from a single-action command into a parent with four subcommands. The parent's default action remains the existing "add" behavior, so the historical shorthand keeps working.
| Command | Purpose |
|---|---|
| `tea comment add <idx> [<body>]` | Add a comment (explicit subcommand) |
| `tea comment list <idx>` | Tabular listing including comment IDs |
| `tea comment edit <id> [<body>]` | Replace the body of one comment |
| `tea comment delete <id> [<id>...]` | Delete one or more comments |
| `tea comment <idx> [<body>]` | Unchanged — still routes to `add` |
The `list` command exists specifically so users can discover the IDs that `edit` and `delete` accept.
## Backward compatibility
The whole point of routing the parent's default `Action` through `add` is to preserve every existing invocation. `tea comment 1 "body"` still does what it did before. No flag or arg names change.
## Input forms (for add and edit)
Same pattern as the original `tea comment`:
1. Positional body (`tea comment edit <id> "new body"`) — wins if present.
2. Piped stdin if no positional body is given.
3. External `$EDITOR` (pre-populated with the current body, on `edit`) if neither.
This matches the stdin-handling fix in #1011 — positional body wins over a non-TTY stdin so the command doesn't hang in CI/subshells.
## Verification
All four subcommands were exercised live against `https://gitea.com/dinsmoor/tea-testing` issue #1. The test artifacts and a summary log are visible on that issue right now. Specifically:
- The annotated summary comment lists every operation tested and the comment IDs each one acted on.
- Comments 1197162 (legacy add), 1197163 (subcommand add, later edited), 1197164 (stdin add) are still there to be inspected.
- Comment 1197166 was created and then deleted; its absence from `tea comment list` output is evidence that delete works.
## New files
- `cmd/comments/add.go` — extracted from the old `cmd/comment.go`
- `cmd/comments/list.go`
- `cmd/comments/edit.go`
- `cmd/comments/delete.go`
- `modules/print/comment.go` — adds `CommentsList` helper for the tabular output
`cmd/comment.go` is rewritten as a thin parent that wires these together.
## Open questions for the reviewer
- **Naming**: should the top-level command be `comments` (plural) or stay `comment` (singular)? I kept it singular with `comments` as an alias to match the existing user-visible name.
- **Delete confirmation**: I did not add a confirmation prompt — `delete` just deletes. Some projects gate this behind `--yes` / interactive `[y/N]`. I'd rather follow whatever convention the maintainers prefer.
- **Output format on list**: currently uses the existing `print.tableWithHeader` helper, matching `tea organizations list` etc. Other tea listings support `--output json` / `--output csv` via the shared `--output` flag, which works here automatically through the same helper.
---
This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step.
*pull request created by Tyler's lovingly wrangled demon machine <3*
Reviewed-on: https://gitea.com/gitea/tea/pulls/1015
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: dinsmoor <204368+dinsmoor@noreply.gitea.com>
Co-committed-by: dinsmoor <204368+dinsmoor@noreply.gitea.com>
Closes#979. An alternative to the approach in #980 — this one is purely client-side title-mangling, no SDK changes needed.
Gitea already treats any PR with a `WIP:` or `[WIP]` title prefix (case-insensitive) as a draft. This patch wires three flags around that behavior:
- `tea pulls create --draft` — prepend `WIP: ` to the title at creation time
- `tea pulls edit --draft <idx>` — add `WIP: ` to an existing PR's title
- `tea pulls edit --ready <idx>` — strip any recognized draft prefix
All three are idempotent. `--draft` and `--ready` on edit are mutually exclusive. If the user also passes `--title` on edit, the toggle applies to the supplied title; otherwise the current title is fetched from the server first.
Why this approach over a server-payload-based one: Gitea's draft state is *defined* as the title-prefix convention (see the Gitea source for `HasWIPPrefix`). Modeling it server-side would either duplicate or fight that. A small string helper covers it without needing the SDK to add a `Draft` field.
Verified against `gitea.com` (1.26.0+dev) with a throwaway repo:
- create with `--draft` → server reports `draft: true` ✓
- `edit --ready` strips → `draft: false` ✓
- `edit --draft` adds back → `draft: true` ✓
- second `edit --draft` is idempotent ✓
- `edit --draft --ready` errors ✓
Unit tests for the prefix detection live in `modules/utils/draft_test.go`.
---
This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step. Reproduction, design decisions, and the choice not to follow #980's payload approach were mine — happy to discuss any of it.
*pull request created by Tyler's lovingly wrangled demon machine <3*
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/1008
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Tyler <tyler@dinsmoor.us>
Co-committed-by: Tyler <tyler@dinsmoor.us>
Closes#1009.
## The standard fork-flow
The textbook workflow for opening a PR with any git CLI (gh, glab, hub, tea, etc.) is:
1. Fork `upstream/repo` on the server.
2. Clone the fork locally; add `upstream` as a second remote.
3. Branch off, commit, push the branch to your fork.
4. Tell the tool: "open a PR on `upstream/repo`, source is `fork:branch`".
In tea that's:
```
tea pulls create --repo upstream/repo --head fork:branch --base main
```
This is the flow tea supported before #850 and the flow this repo's own contribution model assumes.
## What broke
#850 ("Enable git worktree support and improve pr create error handling", Nov 2025) correctly addressed worktree detection by adding `LocalRepo: true` to `runPullsCreate`'s `CtxRequirement`. But that requirement is checked before `--repo` is interpreted, and a slug-style `--repo gitea/tea` doesn't satisfy `LocalRepo` — only a path does.
Result: the standard fork-flow invocation above started failing with:
```
Error: local repository required: execute from a repo dir, or specify a path with --repo
```
— which is misleading, because the user IS in a repo dir and `--repo` IS set.
## The fix
Only require `LocalRepo` when the command genuinely needs the working tree:
- **Interactive mode** (no flags) — uses tree info for prompts.
- **`--head` omitted** — defaults head from the current branch.
Otherwise the command runs with just `RemoteRepo`, and `task.CreatePull` takes the explicit `--repo` and `--head` as given. The #850 worktree fix is preserved for the in-tree path.
## Behavior table
| Invocation | Before | After |
|---|---|---|
| `tea pulls create` (interactive) | works | works (unchanged) |
| `tea pulls create --head my-branch --base main` (in-tree, same repo) | works | works (unchanged) |
| `tea pulls create --repo upstream/repo --head fork:branch --base main` (fork-flow) | **fails with misleading error** | **works** |
| `tea pulls create --repo upstream/repo --base main` (no `--head`, no working tree) | fails | still fails with the same error (correctly — head can't be defaulted without a working tree or explicit flag) |
## Verification
Smoke-tested by opening a PR from `/tmp` (deliberately not a git repo):
```
cd /tmp
tea-dev pulls create --login gitea.com --repo dinsmoor/tea-testing \
--head dinsmoor:xfork-smoke --base main \
--title "xfork smoke test" --description "..."
→ https://gitea.com/dinsmoor/tea-testing/pulls/4
```
And in fact, **this very PR was opened using the patched binary** invoked from `/tmp`, since the released `tea` still has the bug.
---
This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step.
*pull request created by Tyler's lovingly wrangled demon machine <3*
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/1010
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Tyler <tyler@dinsmoor.us>
Co-committed-by: Tyler <tyler@dinsmoor.us>
## The bug
`tea comment <idx> "body"` hangs forever in any non-TTY context — CI pipelines, subshells, agent harnesses, scripts — unless the user explicitly redirects stdin with `< /dev/null`.
### Reproduction (released `tea` 0.14.1)
```
# Plain positional body — hangs forever
$ tea comment --repo owner/repo 1 "body text"
# Heredoc body — hangs forever
$ tea comment --repo owner/repo 1 "$(cat <<'EOM'
body text
EOM
)"
# Workaround that works (but shouldn't be necessary)
$ tea comment --repo owner/repo 1 "body text" < /dev/null
```
Verified on `gitea.com` (server 1.26.0+dev) with `tea 0.14.1`. Both hanging cases hit a 30s timeout in testing; in normal shell use they hang indefinitely.
## Root cause
`cmd/comment.go:58-65`:
```go
body := strings.Join(ctx.Args().Tail(), " ")
if interact.IsStdinPiped() {
if bodyStdin, err := io.ReadAll(ctx.Reader); err != nil {
return err
} else if len(bodyStdin) != 0 {
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
}
}
```
`interact.IsStdinPiped()` is implemented as `!term.IsTerminal(os.Stdin)` — true for *any* non-TTY stdin, not just for piped data. When tea enters this branch in a subshell where stdin is open but no producer ever writes to it (the typical case for shell scripts and automation), `io.ReadAll` blocks waiting for an EOF that never arrives.
## Fix
Only consume stdin when the user did **not** supply a positional body. If they passed a body via args, that's their content — ignore stdin entirely.
```go
if len(body) == 0 && interact.IsStdinPiped() {
// ... read stdin ...
body = string(bodyStdin)
}
```
## Behavior matrix
| Invocation | Before | After |
|---|---|---|
| `tea comment <idx> "body"` (TTY) | works | works |
| `tea comment <idx> "body"` (non-TTY, no redirect) | **hangs forever** | **works** |
| `tea comment <idx> "body" < /dev/null` | works | works |
| `echo body \| tea comment <idx>` | works | works |
| `tea comment <idx> "prefix"` + piped stdin | "prefix" + stdin concatenated | positional body wins, stdin ignored |
| `tea comment <idx>` (TTY, no body, no pipe) | opens editor | opens editor |
The one behavior change is the prefix-plus-stdin case (5th row). I couldn't find anyone relying on that pattern and it wasn't documented; defaulting to "positional body wins" matches the principle of least surprise.
## Verification
Confirmed each row against `dinsmoor/tea-testing` issue #1 on `gitea.com` with the patched binary. Previously-hanging invocations now post in <0.5s. Piped-stdin path unchanged.
---
This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step.
*pull request created by Tyler's lovingly wrangled demon machine <3*
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/1011
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Tyler <tyler@dinsmoor.us>
Co-committed-by: Tyler <tyler@dinsmoor.us>
Closes#1013.
## Background
While trying to use `tea` from a non-interactive context I hit a friction point: after `tea login add` succeeded, plain `git push` over HTTPS still prompted for credentials. I filed #1013 as a feature request to add credential helper integration — then found, on reading the source, that the integration **already exists**:
- `tea login add` accepts `--helper` (alias `-j`), which calls `task.SetupHelper` to register a credential helper in `~/.gitconfig`.
- `tea login helper get` correctly implements git's credential protocol, reading the request from stdin and returning `protocol`/`host`/`username`/`password` lines.
- `tea login helper setup` does the same for every configured login.
I verified end-to-end that this works as advertised: after `tea login helper setup`, an HTTPS `git push` against a configured Gitea host authenticates silently using the stored token, no prompts, with `GIT_TERMINAL_PROMPT=0` set as a safety check.
So the feature is fine. The problem is that nobody can find it:
| Surface | Before | Issue |
|---|---|---|
| Flag name on `tea login add` | `--helper` (alias `-j`) | Generic; nothing tying it to git or credentials |
| Flag usage text | `"Add helper"` | Says nothing |
| `tea login helper` command | `Hidden: true` | Not in `tea login --help` |
| `tea login helper` usage | `"Git helper"` | Says nothing |
| `tea login helper` description | `"Git helper"` | Same string again |
| `store/erase` subcommand description | `"Command drops"` | Sentence fragment, no meaning |
| `setup` subcommand description | `"Setup helper to tea authenticate"` | Awkward, doesn't explain what it touches |
| `get` subcommand description | `"Get token to auth"` | Doesn't mention git, stdin, or the credential protocol |
| Mention in `tea login add --help` | None | Feature is invisible |
## What this patch does
Purely cosmetic / documentation changes — **no behavior changes**:
1. Renames `--helper` to `--git-credentials`, keeping `--helper` and `-j` as aliases so existing scripts and muscle memory keep working.
2. Removes `Hidden: true` from `tea login helper` so it appears in `tea login --help`.
3. Rewrites every placeholder `Usage` and `Description` string in the helper command tree to describe what the thing actually does.
4. Expands the top-level `Description` of `tea login add` to mention the option and explain what it does.
5. Prints a one-line hint after a successful non-helper login: `Tip: pass --git-credentials (or run 'tea login helper setup') to authenticate 'git push' and 'git clone' over HTTPS with this token.`
The credential helper protocol implementation, `SetupHelper`'s gitconfig writes, and the `get`/`store`/`setup` action functions are all unchanged.
## Help output after the patch
```
$ tea login --help
COMMANDS:
...
helper, git-credential Act as a git credential helper for stored Gitea logins
...
$ tea login helper --help
NAME:
tea logins helper - Act as a git credential helper for stored Gitea logins
DESCRIPTION:
Speaks git's credential helper protocol so that HTTPS push and clone
operations against your configured Gitea instances authenticate silently
using the tokens tea already stores.
Typical use is automatic: 'tea login add --git-credentials' (or 'tea login
helper setup' for existing logins) registers '!tea login helper' as a
credential helper in ~/.gitconfig. Git then invokes the 'get' subcommand
when it needs credentials for a configured host.
COMMANDS:
store, erase No-op (git credential protocol store/erase)
setup Register tea as a git credential helper for every configured login
get Return the stored token for a URL (git credential protocol)
```
## Open questions for the reviewer
A few choices in here that are subjective — happy to change any of them:
- **Flag name**: `--git-credentials` was the first clear name I tried. `--credential-helper` and `--git-helper` are also reasonable. Or keep `--helper` as canonical and just fix its usage text.
- **Canonical subcommand name**: I kept `helper` as canonical with `git-credential` as alias, matching what was already there. Could flip this — `gh` uses `gh auth git-credential` as canonical with no `helper` form.
- **Should `--git-credentials` default to true?** Most users probably want it on; the current opt-in design surprises them. But flipping the default is a behavior change so I left it alone here.
- **Should the hint be silenced by an env var or a config flag?** I left it always-on for the people who need to see it; can gate it if it bothers automation users.
- **Teardown on `tea login delete`** would parallel the setup behavior, but is genuinely a separate change. Not in this PR.
---
This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step.
*pull request created by Tyler's lovingly wrangled demon machine <3*
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/1014
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Tyler <tyler@dinsmoor.us>
Co-committed-by: Tyler <tyler@dinsmoor.us>
## Summary
Add first-class `tea wiki` commands backed by the existing Gitea wiki API and SDK support.
## What this adds
- `tea wiki list`
- `tea wiki view <page>`
- `tea wiki revisions <page>`
- `tea wiki create`
- `tea wiki edit <page>`
- `tea wiki delete <page>`
## Implementation details
- registers a new top-level `wiki` entity command
- keeps command logic under `cmd/wiki/`
- adds wiki-specific renderers in `modules/print/wiki.go`
- adds wiki task helpers in `modules/task/wiki.go`
- reuses existing repo/login/output/pagination patterns used elsewhere in `tea`
- base64-encodes wiki content for create/edit API calls
- requires explicit `--confirm` for delete
- preserves the current page title during edit when `--title` is omitted
## Test coverage
The PR is intentionally split into two commits:
1. `feat: add wiki CLI commands`
2. `test: add wiki integration coverage`
Validation performed:
- focused command, task, and print tests for the new wiki functionality
- integration coverage for the wiki command lifecycle
- `make lint`
- `make fmt-check`
- `make docs-check`
- `make build`
- upstream PR CI passed:
- `check-and-test / Integration Test`
- `check-and-test / Lint Build And Unit Coverage`
## Motivation
This makes `tea` a better interface for both human and agent-driven workflows by exposing wiki operations as stable first-class CLI commands instead of requiring ad-hoc API calls or custom wrappers.
---
Generated by Hermes Agent with GPT-5.4
---------
Co-authored-by: nitro <nitro@nitroui-Macmini.local>
Reviewed-on: https://gitea.com/gitea/tea/pulls/998
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: kuil09 <202447+kuil09@noreply.gitea.com>
Co-committed-by: kuil09 <202447+kuil09@noreply.gitea.com>
## What this PR does
`tea issues list --assignee USERNAME` currently returns every issue regardless of the assignee value — even nonexistent users return a full unfiltered list. Discovered against **tea v0.14.0** (with go-sdk v0.24.1) and reproduced on current `master` (commit `dd81b33`). This PR fixes that.
## Root cause
Two distinct bugs on the same flag, both in `cmd/issues/list.go`:
1. **Per-repo path** (`tea issues list --repo OWNER/REPO --assignee USER`): the code reads `ctx.String("assigned-to")` for `AssignedBy`, but the flag is defined as `--assignee` in `cmd/flags/issue_pr.go:66`. The lookup always returns `""`, so the SDK omits the `assigned_by` query parameter and the API returns everything.
2. **Global path** (`tea issues list --assignee USER`, no `--repo`): this hits `/repos/issues/search`, which silently ignores `assigned_by`. Even after fix#1 the no-repo form would still return unfiltered results. Verified directly:
- `GET /repos/issues/search?assigned_by=USER&owner=ORG&state=open` → all open issues
- `GET /repos/issues/search?assigned=true&owner=ORG&state=open` → only the issues assigned to the authenticated user
The endpoint only supports `assigned=true` (boolean self-filter), not arbitrary-user filtering, and `ListIssueOption` doesn't expose that field. Rather than misleading the caller, the no-repo path now returns a clear error.
## Changes
Both changes are in `cmd/issues/list.go`:
1. Read `ctx.String("assignee")` instead of the non-existent flag name `"assigned-to"` (lines 80 and 97).
2. In the no-`--repo` branch, return `errors.New("--assignee requires --repo (...)")` when the flag is set.
`cmd/pulls/list.go` does not expose an assignee filter, so it's unaffected. The `--author` mapping (`CreatedBy ← ctx.String("author")`) was already correct and is the model the fix follows.
## Manual verification
Tested against a local Gitea instance with three open issues (only one assigned to the test user):
| Command | Before | After |
|---|---|---|
| `tea issues list --repo X --assignee me` | all 3 | only the 1 assigned ✓ |
| `tea issues list --repo X --assignee nonexistent` | all 3 | `Error: not found` ✓ |
| `tea issues list --repo X --author me` | only the 1 (control) | unchanged ✓ |
| `tea issues list --assignee me` (no `--repo`) | all 3 (silent) | clear error ✓ |
| `tea issues list` (no flags) | all 3 | unchanged ✓ |
---------
Co-authored-by: claude_1 <claude_1@bot.gqx.lol>
Reviewed-on: https://gitea.com/gitea/tea/pulls/971
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Oleksii Zaremskyi <grossqx@gmail.com>
Co-committed-by: Oleksii Zaremskyi <grossqx@gmail.com>
## Summary
\`Page: -1\` in the Gitea SDK calls \`setDefaults()\` which sets both \`Page=0\` and \`PageSize=0\`, resulting in \`?page=0&limit=0\` being sent to the server. The server interprets \`limit=0\` as "use server default" (typically 30 items via \`DEFAULT_PAGING_NUM\`), not "return everything". Any resource beyond the first page of results was silently invisible.
This affected 8 call sites, with the most user-visible impact being \`tea issues edit --add-labels\` and \`tea pulls edit --add-labels\` silently failing to apply labels on repositories with more than ~30 labels.
## Affected call sites
| File | API call | User-visible impact |
|---|---|---|
| \`modules/task/labels.go\` | \`ListRepoLabels\` | \`issues/pulls edit --add-labels\` fails silently |
| \`modules/interact/issue_create.go\` | \`ListRepoLabels\` | interactive label picker missing labels |
| \`modules/task/pull_review_comment.go\` | \`ListPullReviews\` | review comments truncated |
| \`modules/task/login_ssh.go\` | \`ListMyPublicKeys\` | SSH key auto-detection fails |
| \`modules/task/login_create.go\` | \`ListAccessTokens\` | token name deduplication misses existing tokens |
| \`cmd/pulls.go\` | \`ListPullReviews\` | PR detail view missing reviews |
| \`cmd/releases/utils.go\` | \`ListReleases\` | tag lookup fails on repos with many releases |
| \`cmd/attachments/delete.go\` | \`ListReleaseAttachments\` | attachment deletion fails when many attachments exist |
## Fix
Each call site is replaced with an explicit pagination loop that follows \`resp.NextPage\` until all pages are exhausted.
Reviewed-on: https://gitea.com/gitea/tea/pulls/967
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Alain Thiffault <athiffau@effectivemomentum.com>
Co-committed-by: Alain Thiffault <athiffau@effectivemomentum.com>
## Summary
- Add `"ci"` as a new selectable field for `tea pr list --fields`, allowing users to see CI status across multiple PRs at a glance
- Fetch CI status via `GetCombinedStatus` API **only when the `ci` field is explicitly requested** via `--fields`, avoiding unnecessary API calls in default usage
- Improve CI status display in both detail and list views:
- **Detail view** (`tea pr <index>`): show each CI check with symbol, context name, description, and clickable link to CI run
- **List view** (`tea pr list --fields ci`): show symbol + context name per CI check (e.g., `✓ lint, ⏳ build, ❌ test`)
- **Machine-readable output**: return raw state string (e.g., `success`, `pending`)
- Replace pending CI symbol from `⭮` to `⏳` for better readability
- Extract `formatCIStatus` helper and reuse it in `PullDetails` to reduce code duplication
- Add comprehensive tests for CI status formatting and PR list integration
## Detail View Example
```
- CI:
- ✓ [**lint**](https://ci.example.com/lint): Lint passed
- ⏳ [**build**](https://ci.example.com/build): Build is running
- ❌ [**test**](https://ci.example.com/test): 3 tests failed
```
## List View Example
```
INDEX TITLE STATE CI
123 Fix bug open ✓ lint, ⏳ build, ❌ test
```
## Usage
```bash
# Show CI status column in list
tea pr list --fields index,title,state,ci
# Default output is unchanged (no CI column, no extra API calls)
tea pr list
```
## Files Changed
- `cmd/pulls/list.go` — conditionally fetch CI status per PR when `ci` field is selected
- `modules/print/pull.go` — add `ci` field, `formatCIStatus` helper, improve detail/list CI display
- `modules/print/pull_test.go` — comprehensive tests for CI status formatting
## Test plan
- [x] `go build ./...` passes
- [x] `go test ./...` passes (11 new tests)
- [x] `tea pr list` — default output unchanged, no extra API calls
- [x] `tea pr list --fields index,title,state,ci` — CI column with context names
- [x] `tea pr <index>` — CI section shows each check with name, description, and link
- [x] `tea pr list --fields ci -o csv` — machine-readable output shows raw state strings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: https://gitea.com/gitea/tea/pulls/956
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
## Summary
- Extract duplicate \`getReleaseByTag\` into shared \`cmd/releases/utils.go\`
- Replace \`log.Fatal\` calls with proper error returns in config and login commands; \`GetLoginByToken\`/\`GetLoginsByHost\`/\`GetLoginByHost\` now return errors
- Remove dead \`portChan\` channel in \`modules/auth/oauth.go\`
- Fix YAML integer detection to use \`strconv.ParseInt\` (correctly handles negatives and large ints)
- Fix \`path.go\` error handling to use \`errors.As\` + \`syscall.ENOTDIR\` instead of string comparison
- Extract repeated credential helper key into local variable in \`SetupHelper\`
- Use existing \`isRemoteDeleted()\` in \`pull_clean.go\` instead of duplicating the logic
- Fix ~30 error message casing violations to follow Go conventions
- Use \`fmt.Errorf\` consistently instead of string concatenation in \`generic.go\`
Reviewed-on: https://gitea.com/gitea/tea/pulls/947
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Bo-Yi Wu (吳柏毅) <appleboy.tw@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-committed-by: Nicolas <bircni@icloud.com>
- Add an owner flag to the repos list command to list repositories for a specific user or organization
- Implement owner-based repository listing by detecting whether the owner is an organization or a user and calling the appropriate API
- Improve error handling for owner lookup by checking HTTP status codes instead of relying on error string matching
- Align repository search logic with the updated owner lookup behavior using HTTP response validation
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/931
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
## Summary
- Add `tea repo edit` subcommand to update repository properties via the Gitea API
- Support flags: `--name`, `--description`/`--desc`, `--website`, `--private`, `--template`, `--archived`, `--default-branch`
- Boolean-like flags use string type to distinguish "not set" from "false", following the pattern in `releases/edit.go`
## Test plan
- [x] `go build ./...` passes
- [x] `go vet ./...` passes
- [x] `tea repo edit --help` shows all flags correctly
- [x] Manual test: `tea repo edit --private true` on a test repo
- [x] Manual test: `tea repo edit --name new-name --description "new desc"` on a test repo
Reviewed-on: https://gitea.com/gitea/tea/pulls/928
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
## Summary
- Introduce `github.com/go-authgate/sdk-go/credstore` to store OAuth tokens securely in the OS keyring (macOS Keychain / Linux Secret Service / Windows Credential Manager), with automatic fallback to an encrypted JSON file
- Add `AuthMethod` field to `Login` struct; new OAuth logins are marked `auth_method: oauth` and no longer write `token`/`refresh_token`/`token_expiry` to `config.yml`
- Add `GetAccessToken()` / `GetRefreshToken()` / `GetTokenExpiry()` accessors that transparently read from credstore for OAuth logins, with fallback to YAML fields for legacy logins
- Update all token reference sites across the codebase to use the new accessors
- Non-OAuth logins (token, SSH) are completely unaffected; no migration of existing tokens
## Key files
| File | Role |
|------|------|
| `modules/config/credstore.go` | **New** — credstore wrapper (Load/Save/Delete) |
| `modules/config/login.go` | Login struct, token accessors, refresh logic |
| `modules/auth/oauth.go` | OAuth flow, token creation / re-authentication |
| `modules/api/client.go`, `cmd/login/helper.go`, `cmd/login/oauth_refresh.go` | Token reference updates |
| `modules/task/pull_*.go`, `modules/task/repo_clone.go` | Git operation token reference updates |
## Test plan
- [x] `go build ./...` compiles successfully
- [x] `go test ./...` all tests pass
- [x] `tea login add --oauth` completes OAuth flow; verify config.yml has `auth_method: oauth` but no token/refresh_token/token_expiry
- [x] `tea repos ls` API calls work (token read from credstore)
- [x] `tea login delete <name>` credstore token is also removed
- [x] Existing non-OAuth logins continue to work unchanged
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: https://gitea.com/gitea/tea/pulls/926
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
- switch to golangci-lint for linting
- switch to gofmpt for formatting
- fix lint and fmt issues that came up from switch to new tools
- upgrade go-sdk to 0.23.2
- support pagination for listing tracked times
- remove `FixPullHeadSha` workaround (upstream fix has been merged for 5+ years at this point)
- standardize on US spelling (previously a mix of US&UK spelling)
- remove some unused code
- reduce some duplication in parsing state and issue type
- reduce some duplication in reading input for secrets and variables
- reduce some duplication with PR Review code
- report error for when yaml parsing fails
- various other misc cleanup
Reviewed-on: https://gitea.com/gitea/tea/pulls/869
Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-committed-by: techknowlogick <techknowlogick@gitea.com>
## Summary
Fix the `tea labels delete` and `tea labels update` commands which were silently ignoring the `--id` flag.
## Problem
Both commands used `IntFlag` for the `--id` parameter but called `ctx.Int64("id")` to retrieve the value. This type mismatch caused the ID to always be read as `0`, making the commands useless.
**Before (bug):**
```bash
$ tea labels delete --id 36 --debug
DELETE: .../labels/0 # Wrong! ID ignored
```
**After (fix):**
```bash
$ tea labels delete --id 36 --debug
GET: .../labels/36 # Verify exists
DELETE: .../labels/36 # Correct ID
Label 'my-label' (id: 36) deleted successfully
```
## Changes
### labels/delete.go
- Change `IntFlag` to `Int64Flag` to match `ctx.Int64()` usage
- Make `--id` flag required
- Verify label exists before attempting deletion
- Provide clear error messages with label name and ID context
- Print success message after deletion
### labels/update.go
- Change `IntFlag` to `Int64Flag` to fix the same bug
## Test plan
- [x] `go test ./...` passes
- [x] `go vet ./...` passes
- [x] `gofmt` check passes
- [x] Manual testing confirms ID is now correctly passed to API
- [ ] CI passes
Reviewed-on: https://gitea.com/gitea/tea/pulls/865
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Alain Thiffault <athiffau@effectivemomentum.com>
Co-committed-by: Alain Thiffault <athiffau@effectivemomentum.com>