refactor: turn the repo into a two-plugin marketplace
tea and tdl were two repositories, each carrying its own
.claude-plugin/marketplace.json — two marketplaces to register for what
is one collection. Fold them into one.
The repo root is now the marketplace and nothing else: a single
.claude-plugin/marketplace.json whose entries point at ./plugins/tea and
./plugins/tdl. A plugin's root is its own directory under plugins/, so
${CLAUDE_PLUGIN_ROOT} still resolves inside it and every path a plugin
uses stays relative to itself — the hooks and the test roots needed no
adjustment beyond the move.
tea's files move with git mv, so its history and blame follow. tdl
arrives as a plain copy; its history stays in claude-skills/threedotslab.
test_payload_root asserted `tmp/` was ignored by REPO/.gitignore. The
rule is that tmp/ is ignored, not which file says so, and git reads every
.gitignore on the way up — so the test now walks up to the repo root the
same way git does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
---
|
||||
name: auth
|
||||
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:auth. Enumerates available logins, makes the OPERATOR pick one, and persists it to .claude/settings.local.json. The pin takes effect immediately — no restart.
|
||||
---
|
||||
|
||||
# /tea:auth — pin the project Gitea login
|
||||
|
||||
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 with no pin):
|
||||
`tea logins list -o json`
|
||||
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 the **project root's**
|
||||
`.claude/settings.local.json` under `env` (do not clobber other keys):
|
||||
```json
|
||||
{ "env": { "GITEA_LOGIN": "<chosen-name>" } }
|
||||
```
|
||||
**In a git worktree, write it to the main checkout, never to the worktree.**
|
||||
A worktree is deleted when the branch is done, taking a pin written into it
|
||||
with it, and one repository with two pins is one repository with two
|
||||
identities. Both the guard and the scripts already reach the main checkout's
|
||||
pin from inside any worktree — so there is nothing to pin a second time.
|
||||
`git rev-parse --path-format=absolute --git-common-dir` names the `.git` to
|
||||
write beside.
|
||||
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,
|
||||
and which file it went in.
|
||||
|
||||
## Where the pin is looked for
|
||||
|
||||
One search order, written once in `scripts/pin.py` and imported by both the
|
||||
`tea-guard` hook and the sync transport — they cannot disagree about a
|
||||
directory, and a test asserts neither keeps a copy of the walk.
|
||||
|
||||
`$CLAUDE_PROJECT_DIR`, then the caller's hint (the hook passes the Bash call's
|
||||
`cwd`), then the current directory. Each is searched up its parent chain; only
|
||||
if that finds nothing does the search cross into the main working tree of a
|
||||
linked worktree, via `gitdir:` in the `.git` file. The plugin's own directory
|
||||
is never a source — a plugin pointed at somebody else's project must take the
|
||||
identity from that project, not from where it happens to be installed.
|
||||
|
||||
If a script reports "no login pinned", that is the honest answer: nothing was
|
||||
found anywhere on that order. Pin one — at the project root.
|
||||
|
||||
## 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. 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.
|
||||
Reference in New Issue
Block a user