tea-guard: resolve and rewrite --login instead of env-checking
The previous guard required $GITEA_LOGIN to be set in the Bash environment,
which (a) only happens after a session restart and (b) let Claude name any
login it liked as long as one was set. Two failures: pinning needed a restart
to take effect, and Claude could pick the wrong identity from memory.
Rewrite the guard (now python3 for JSON in/out) to RESOLVE the login itself:
- Claude must write the literal placeholder --login "$GITEA_LOGIN".
- The hook reads the operator's pin from .claude/settings.local.json
(env.GITEA_LOGIN) at call time — from the FILE via CLAUDE_PROJECT_DIR/cwd
walk-up — and rewrites the command to that literal via updatedInput.
- A literal login, another variable, an empty value, or a missing --login are
all blocked: Claude may not choose the identity, only the operator may.
- No pin -> block with a pointer to /tea:login.
Effect: pinning works in the same session (no restart), and Claude can no
longer act under a login it picked. /tea:login now mandates an explicit
operator choice (AskUserQuestion), never inferring from memory. /tea:use
documents the placeholder-only contract.
Guard unit-tested across 13 rewrite/block/passthrough cases incl. -l,
--login=, ${...}, compound+walkup+pipe. claude plugin validate passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+33
-22
@@ -1,40 +1,51 @@
|
||||
---
|
||||
name: login
|
||||
description: Pin the Gitea login used by the tea CLI in this project. Run when $GITEA_LOGIN is unset, when the tea-guard hook blocks a command demanding a login, or when the user types /tea:login. Enumerates available logins, lets the user pick one, and persists it to .claude/settings.local.json.
|
||||
description: Pin the Gitea login used by the tea CLI in this project. Run when the tea-guard hook reports no login is pinned, or when the user types /tea:login. Enumerates available logins, makes the OPERATOR pick one, and persists it to .claude/settings.local.json. The pin takes effect immediately — no restart.
|
||||
---
|
||||
|
||||
# /tea:login — pin the project Gitea login
|
||||
|
||||
Goal: select exactly one `tea` login for this project and persist it to
|
||||
`.claude/settings.local.json` under `env.GITEA_LOGIN`, so every later `tea`
|
||||
call can pass `--login "$GITEA_LOGIN"`. The `tea-guard` hook blocks every
|
||||
Gitea-touching `tea` command until this is done — this command is the
|
||||
remediation it points to.
|
||||
Goal: have the **operator** select exactly one `tea` login for this project and
|
||||
persist it to `.claude/settings.local.json` under `env.GITEA_LOGIN`. The
|
||||
`tea-guard` hook reads this file at call time and rewrites every
|
||||
`--login "$GITEA_LOGIN"` to the pinned value, so the choice takes effect
|
||||
**immediately, with no session restart**.
|
||||
|
||||
## The one hard rule: the operator chooses, never you
|
||||
|
||||
Picking the wrong identity is the exact failure this command exists to prevent.
|
||||
So:
|
||||
|
||||
- **ALWAYS** present the choice with `AskUserQuestion` and let the operator
|
||||
pick — even if memory, context, the repo URL, or a previous session suggests
|
||||
a "likely" login. Do **not** auto-select from memory or infer it. A wrong
|
||||
guess writes under the wrong account.
|
||||
- The only exception: exactly **one** login exists on the machine — then
|
||||
propose it and still confirm before writing.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Enumerate logins (allowed by the guard even without `--login`):
|
||||
1. Enumerate logins (allowed by the guard even with no pin):
|
||||
`tea logins list -o json`
|
||||
2. **No logins:** stop and ask the user to run `tea logins add` themselves —
|
||||
it is interactive (prompts for URL/token). Do not run it for them.
|
||||
3. **One login:** propose pinning it; confirm with the user before writing.
|
||||
4. **Several logins:** use `AskUserQuestion` to let the user pick. Show each
|
||||
login's `name`, `user`, and `url` so the choice is unambiguous.
|
||||
5. Merge the chosen name into `.claude/settings.local.json` under `env` —
|
||||
do not clobber other keys:
|
||||
2. **No logins:** stop and ask the operator to run `tea logins add` themselves
|
||||
— it is interactive (prompts for URL/token). Do not run it for them.
|
||||
3. **One login:** propose it; confirm before writing.
|
||||
4. **Several logins:** `AskUserQuestion` with each login's `name`, `user`, and
|
||||
`url` so the operator's choice is unambiguous. Never decide for them.
|
||||
5. Merge the chosen name into `.claude/settings.local.json` under `env`
|
||||
(do not clobber other keys):
|
||||
```json
|
||||
{ "env": { "GITEA_LOGIN": "<chosen-name>" } }
|
||||
```
|
||||
6. Tell the user: the updated `$GITEA_LOGIN` is only exported into Bash after a
|
||||
**session restart**. Until they restart, pass the literal name explicitly:
|
||||
`tea --login <chosen-name> ...`.
|
||||
6. Done — it is live. The guard resolves the pin from the file on the next
|
||||
`tea` call; no restart needed. Tell the operator which login is now pinned.
|
||||
|
||||
## Hard rules (identity safety)
|
||||
## Identity-safety rules
|
||||
|
||||
- NEVER run commands that mutate logins or global login state:
|
||||
`tea logins add/edit/delete/default`, `tea logout`. Read-only
|
||||
`tea logins list` is the only allowed login command.
|
||||
- If a `tea` call fails with a permission/scope error, report it to the user.
|
||||
Do NOT try to fix it by switching to, or editing, a different login.
|
||||
- If you ever see `no gitea login detected, falling back to login '...'`,
|
||||
treat it as a hard failure: stop, do not act on the result, surface it.
|
||||
- If a `tea` call fails with a permission/scope error, report it. Do NOT try to
|
||||
fix it by switching to, or editing, a different login.
|
||||
- If you ever see `no gitea login detected, falling back to login '...'`, treat
|
||||
it as a hard failure: stop, do not act on the result, surface it.
|
||||
|
||||
+29
-16
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: use
|
||||
description: Reference docs for the `tea` CLI — Gitea's command-line client. Load when the user asks about Gitea repos, issues, pulls, releases, actions, or other Gitea entities, to look up the right `tea` command and flags. Every tea call must carry --login "$GITEA_LOGIN" (enforced by the tea-guard hook; set it with /tea:login).
|
||||
description: Reference docs for the `tea` CLI — Gitea's command-line client. Load when the user asks about Gitea repos, issues, pulls, releases, actions, or other Gitea entities, to look up the right `tea` command and flags. Always write the login as the literal placeholder --login "$GITEA_LOGIN" — the tea-guard hook substitutes the operator-pinned login; set it with /tea:login.
|
||||
---
|
||||
|
||||
# /tea:use — tea CLI reference
|
||||
@@ -9,18 +9,27 @@ Reference material for the `tea` CLI (Gitea's official command-line client).
|
||||
Use these docs to look up commands, flags, filters, and output fields before
|
||||
running `tea` via Bash.
|
||||
|
||||
## Login is mandatory (enforced)
|
||||
## Login: always write the placeholder, never a name (enforced)
|
||||
|
||||
Every `tea` invocation that touches Gitea MUST include `--login "$GITEA_LOGIN"`
|
||||
(or `-l "$GITEA_LOGIN"`). This is enforced by the **`tea-guard`** PreToolUse
|
||||
hook — a `tea` command without `--login` is blocked before it runs. If
|
||||
`$GITEA_LOGIN` is unset, run **`/tea:login`** to pin one.
|
||||
Every `tea` invocation that touches Gitea MUST carry the login as the **literal
|
||||
placeholder** `--login "$GITEA_LOGIN"` (or `-l "$GITEA_LOGIN"`). Do **not**
|
||||
substitute an actual login name yourself.
|
||||
|
||||
Why: without `--login`, `tea` silently falls back to the machine's default
|
||||
login (possibly the user's personal account) and writes under the wrong
|
||||
identity. The login value is pinned per-project in `.claude/settings.local.json`
|
||||
under `env.GITEA_LOGIN`. Only `tea logins list` and `tea --version/--help` are
|
||||
exempt from the guard.
|
||||
The **`tea-guard`** PreToolUse hook enforces this and resolves it:
|
||||
|
||||
- no `--login` → blocked.
|
||||
- `--login "$GITEA_LOGIN"` → the hook reads the operator's pinned login from
|
||||
`.claude/settings.local.json` (`env.GITEA_LOGIN`) **at call time** and
|
||||
rewrites the command to use that literal before it runs.
|
||||
- `--login <some-name>` or any other variable → blocked. You may not choose the
|
||||
login; only the operator does (via `/tea:login`).
|
||||
- no login pinned → blocked with a pointer to run `/tea:login`.
|
||||
|
||||
Why: without an explicit login `tea` silently falls back to the machine's
|
||||
default (possibly the user's personal account), and a login *you* pick may be
|
||||
the wrong identity. Pinning is the operator's decision; the hook guarantees it.
|
||||
The pin takes effect immediately — no restart. Only `tea logins list` and
|
||||
`tea --version/--help` are exempt from the guard.
|
||||
|
||||
## How to use
|
||||
|
||||
@@ -28,12 +37,14 @@ exempt from the guard.
|
||||
releases, times, repos, branches, actions, webhooks, comments,
|
||||
notifications, etc.
|
||||
2. Find the matching command in the index below.
|
||||
3. Run it via Bash with the login, e.g.
|
||||
3. Run it via Bash with the placeholder login, e.g.
|
||||
`tea issues list --login "$GITEA_LOGIN" --repo owner/repo --state open`.
|
||||
(The hook rewrites `"$GITEA_LOGIN"` to the operator-pinned login.)
|
||||
|
||||
`tea` auto-detects owner/repo from `$PWD` inside a git repo; otherwise pass
|
||||
`--repo owner/repo` (or `-r`). Login is **not** auto-detected — it is pinned
|
||||
per-project (see `/tea:login`). Config lives in `$XDG_CONFIG_HOME/tea`.
|
||||
per-project by the operator (see `/tea:login`) and injected by the guard.
|
||||
Config lives in `$XDG_CONFIG_HOME/tea`.
|
||||
|
||||
## Index
|
||||
|
||||
@@ -91,12 +102,14 @@ request payload to `$PWD/tmp/` first, then POST via `tea api`.
|
||||
| Create release | `POST repos/{owner}/{repo}/releases` |
|
||||
|
||||
Short single-line bodies (e.g. `tea comment 42 "lgtm" --login "$GITEA_LOGIN"`)
|
||||
are still fine via entity commands.
|
||||
are still fine via entity commands. Always the placeholder, never a login name.
|
||||
|
||||
## Tips
|
||||
|
||||
- Pass `-o json` for structured output when parsing programmatically.
|
||||
- Use `--fields, -f` to narrow columns.
|
||||
- Pagination: `--page, -p <n>` and `--limit, --lm <n>` (defaults 1 / 30).
|
||||
- If a `tea` command is blocked by `tea-guard`, you forgot `--login` or
|
||||
`$GITEA_LOGIN` is unset — add the flag or run `/tea:login`.
|
||||
- If a `tea` command is blocked by `tea-guard`: either you forgot
|
||||
`--login "$GITEA_LOGIN"`, you wrote a literal login name instead of the
|
||||
placeholder (not allowed — let the guard substitute), or no login is pinned
|
||||
(run `/tea:login`).
|
||||
|
||||
Reference in New Issue
Block a user