docs: add design spec and implementation plan
This commit is contained in:
@@ -0,0 +1,803 @@
|
||||
# Gitea Connector Plugin — Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Build a Claude Code plugin that lets collaborators connect to Seth's Gitea instance (`git.sethpc.xyz`) with their own accounts, bundling a bash CLI, guided setup, commit conventions, and a push-reminder hook.
|
||||
|
||||
**Architecture:** Claude Code plugin with `.claude-plugin/plugin.json` manifest. Slash command (`/gitea-setup`) invokes an agent for interactive credential setup. Skill provides commit conventions. Hook reminds to push after commits. Bash CLI in `bin/gitea` handles all API/git operations over HTTPS to `git.sethpc.xyz`.
|
||||
|
||||
**Tech Stack:** Bash (CLI), Markdown with YAML frontmatter (plugin components), JSON (plugin manifest, hook config), `curl`/`jq`/`git` (CLI dependencies).
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-04-01-gitea-connector-design.md`
|
||||
|
||||
---
|
||||
|
||||
## File Map
|
||||
|
||||
| File | Action | Responsibility |
|
||||
|------|--------|----------------|
|
||||
| `.claude-plugin/plugin.json` | Create | Plugin manifest — name, description, paths to commands/agents/hooks |
|
||||
| `bin/gitea` | Create | Bash CLI — create, remote, push, delete, list repos. HTTPS-only to `git.sethpc.xyz`. Reads username/token/host from `~/.config/gitea/` |
|
||||
| `commands/gitea-setup.md` | Create | `/gitea-setup` slash command — invokes the setup agent |
|
||||
| `agents/gitea-setup.md` | Create | Interactive setup agent — dependency check, credential collection, CLI install, token validation |
|
||||
| `skills/gitea-workflow/SKILL.md` | Create | Commit conventions, CLI reference, credential safety, override mechanism |
|
||||
| `hooks/hooks.json` | Create | Hook configuration — PostToolUse push reminder (disabled by default) |
|
||||
| `hooks/scripts/push-reminder.sh` | Create | Detects `git commit` in Bash tool output, echoes push reminder |
|
||||
| `README.md` | Create | User-facing install + usage docs |
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Plugin Manifest & Scaffold
|
||||
|
||||
**Files:**
|
||||
- Create: `gitea-connector/.claude-plugin/plugin.json`
|
||||
|
||||
- [ ] **Step 1: Create the `.claude-plugin` directory**
|
||||
|
||||
```bash
|
||||
mkdir -p /home/claude/bin/gitea-connector/.claude-plugin
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Write `plugin.json`**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "gitea-connector",
|
||||
"version": "1.0.0",
|
||||
"description": "Connect Claude Code to Seth's Gitea instance (git.sethpc.xyz) — guided setup, CLI, commit conventions, and a gentle nudge to push.",
|
||||
"author": {
|
||||
"name": "Seth",
|
||||
"url": "https://git.sethpc.xyz"
|
||||
},
|
||||
"homepage": "https://git.sethpc.xyz/Seth/gitea-connector",
|
||||
"repository": "https://git.sethpc.xyz/Seth/gitea-connector",
|
||||
"license": "MIT",
|
||||
"commands": ["./commands"],
|
||||
"agents": ["./agents"],
|
||||
"hooks": "./hooks/hooks.json"
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Create remaining directory structure**
|
||||
|
||||
```bash
|
||||
mkdir -p /home/claude/bin/gitea-connector/{commands,agents,skills/gitea-workflow,hooks/scripts,bin}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Init git repo**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector && git init
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Create `.gitignore`**
|
||||
|
||||
```
|
||||
.backup/
|
||||
*.swp
|
||||
*.swo
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Commit scaffold**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
git add .claude-plugin/plugin.json .gitignore
|
||||
git commit -m "chore: scaffold plugin with manifest and directory structure"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Bash CLI (`bin/gitea`)
|
||||
|
||||
**Files:**
|
||||
- Create: `gitea-connector/bin/gitea`
|
||||
|
||||
- [ ] **Step 1: Write the CLI script**
|
||||
|
||||
Adapted from Seth's `~/bin/gitea`. Key changes: no LAN detection, reads username from config, silly help text.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# gitea — your friendly neighborhood Gitea CLI
|
||||
# It creates repos. It pushes code. It occasionally judges your commit messages.
|
||||
# Usage: gitea <command> [options]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# --- Config ---
|
||||
GITEA_HOST="git.sethpc.xyz"
|
||||
if [[ -f "$HOME/.config/gitea/host" ]]; then
|
||||
GITEA_HOST="$(< "$HOME/.config/gitea/host")"
|
||||
GITEA_HOST="${GITEA_HOST%%[[:space:]]}"
|
||||
fi
|
||||
|
||||
GITEA_USER=""
|
||||
if [[ -f "$HOME/.config/gitea/username" ]]; then
|
||||
GITEA_USER="$(< "$HOME/.config/gitea/username")"
|
||||
GITEA_USER="${GITEA_USER%%[[:space:]]}"
|
||||
fi
|
||||
[[ -z "$GITEA_USER" ]] && { echo "ERROR: No username found. Run /gitea-setup or put your username in ~/.config/gitea/username" >&2; exit 1; }
|
||||
|
||||
GITEA_TOKEN="${GITEA_TOKEN:-}"
|
||||
if [[ -z "$GITEA_TOKEN" && -f "$HOME/.config/gitea/token" ]]; then
|
||||
GITEA_TOKEN="$(< "$HOME/.config/gitea/token")"
|
||||
GITEA_TOKEN="${GITEA_TOKEN%%[[:space:]]}"
|
||||
fi
|
||||
[[ -z "$GITEA_TOKEN" ]] && { echo "ERROR: No token found. Run /gitea-setup or put your token in ~/.config/gitea/token" >&2; exit 1; }
|
||||
|
||||
API_BASE="https://${GITEA_HOST}/api/v1"
|
||||
|
||||
# --- Helpers ---
|
||||
api() {
|
||||
local method="$1" endpoint="$2"
|
||||
shift 2
|
||||
curl -sf -X "$method" "${API_BASE}${endpoint}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
# --- Commands ---
|
||||
cmd_create() {
|
||||
local name="" private=false description=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--private|-p) private=true; shift ;;
|
||||
--public) private=false; shift ;;
|
||||
--description|-d) description="$2"; shift 2 ;;
|
||||
-*) echo "Unknown option: $1 (I don't know what that means and I'm too proud to guess)" >&2; exit 1 ;;
|
||||
*) name="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
[[ -z "$name" ]] && { echo "Usage: gitea create <name> [--private] [--description \"...\"]"; echo " (You forgot the name. The repo needs a name. We all need names.)" >&2; exit 1; }
|
||||
|
||||
local payload
|
||||
payload=$(jq -n \
|
||||
--arg name "$name" \
|
||||
--argjson private "$private" \
|
||||
--arg desc "$description" \
|
||||
'{name: $name, private: $private, description: $desc, auto_init: false}')
|
||||
|
||||
local resp
|
||||
resp=$(api POST "/user/repos" -d "$payload") || { echo "FAIL: Gitea said no. Check your token and network." >&2; exit 1; }
|
||||
|
||||
local html_url
|
||||
html_url=$(echo "$resp" | jq -r '.html_url')
|
||||
echo "Created: ${html_url}"
|
||||
echo "Clone: https://${GITEA_HOST}/${GITEA_USER}/${name}.git"
|
||||
echo ""
|
||||
echo "Next up: 'gitea remote ${name}' to wire up your origin. You know the drill."
|
||||
}
|
||||
|
||||
cmd_remote() {
|
||||
local name="${1:-}"
|
||||
[[ -z "$name" ]] && { echo "Usage: gitea remote <repo-name>"; echo " (Which repo? I can't read minds. Yet.)" >&2; exit 1; }
|
||||
|
||||
local url="https://${GITEA_HOST}/${GITEA_USER}/${name}.git"
|
||||
|
||||
git config credential.helper 'store'
|
||||
local cred_file="${HOME}/.git-credentials"
|
||||
local cred_entry="https://${GITEA_USER}:${GITEA_TOKEN}@${GITEA_HOST}"
|
||||
if ! grep -qF "${GITEA_HOST}" "$cred_file" 2>/dev/null; then
|
||||
echo "$cred_entry" >> "$cred_file"
|
||||
chmod 600 "$cred_file"
|
||||
fi
|
||||
|
||||
if git remote get-url origin &>/dev/null; then
|
||||
git remote set-url origin "$url"
|
||||
echo "Updated origin -> ${url}"
|
||||
else
|
||||
git remote add origin "$url"
|
||||
echo "Added origin -> ${url}"
|
||||
fi
|
||||
echo "You're locked and loaded. 'gitea push' when ready."
|
||||
}
|
||||
|
||||
cmd_push() {
|
||||
local branch
|
||||
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || { echo "This isn't a git repo. I checked." >&2; exit 1; }
|
||||
echo "Pushing ${branch} to origin... hold onto your bits."
|
||||
git push -u origin "$branch"
|
||||
}
|
||||
|
||||
cmd_delete() {
|
||||
local name="${1:-}"
|
||||
[[ -z "$name" ]] && { echo "Usage: gitea delete <repo-name>"; echo " (Delete what? Be specific. Destruction requires precision.)" >&2; exit 1; }
|
||||
|
||||
read -rp "Delete ${GITEA_USER}/${name}? This is permanent. No take-backs. [y/N] " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Crisis averted. Repo lives another day."; exit 0; }
|
||||
|
||||
if api DELETE "/repos/${GITEA_USER}/${name}"; then
|
||||
echo "Deleted: ${GITEA_USER}/${name}. It's gone. Pour one out."
|
||||
else
|
||||
echo "FAIL: Could not delete ${GITEA_USER}/${name}. Maybe it's already gone? Maybe it's fighting back?" >&2; exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
local resp
|
||||
resp=$(api GET "/user/repos?limit=50&sort=updated") || { echo "FAIL: Couldn't fetch repos. Is Gitea awake?" >&2; exit 1; }
|
||||
echo "$resp" | jq -r '.[] | "\(.name)\t\(if .private then "private" else "public" end)\t\(.description // "")"' | column -t -s$'\t'
|
||||
}
|
||||
|
||||
cmd_help() {
|
||||
cat <<'EOF'
|
||||
gitea — your friendly neighborhood Gitea CLI
|
||||
|
||||
Usage: gitea <command> [options]
|
||||
|
||||
Commands:
|
||||
create <name> [--private] [--description "..."] Bring a new repo into this world
|
||||
remote <name> Wire up git origin with token auth
|
||||
push Yeet current branch to origin
|
||||
delete <name> Send a repo into the void (confirms first)
|
||||
list Show your repos (sorted by last updated)
|
||||
|
||||
Examples:
|
||||
gitea create my-project --private --description "Top secret stuff"
|
||||
gitea remote my-project
|
||||
gitea push
|
||||
|
||||
Config files (~/.config/gitea/):
|
||||
token Your API token (required)
|
||||
username Your Gitea username (required)
|
||||
host Gitea host override (default: git.sethpc.xyz)
|
||||
|
||||
First time? Run /gitea-setup in Claude Code to get started.
|
||||
EOF
|
||||
}
|
||||
|
||||
case "${1:-help}" in
|
||||
create) shift; cmd_create "$@" ;;
|
||||
remote) shift; cmd_remote "$@" ;;
|
||||
push) shift; cmd_push "$@" ;;
|
||||
delete) shift; cmd_delete "$@" ;;
|
||||
list|ls) shift; cmd_list "$@" ;;
|
||||
help|--help|-h) cmd_help ;;
|
||||
*) echo "Unknown command: $1 (try 'gitea help' — I promise it's helpful)" >&2; exit 1 ;;
|
||||
esac
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Make it executable**
|
||||
|
||||
```bash
|
||||
chmod +x /home/claude/bin/gitea-connector/bin/gitea
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Test CLI help output**
|
||||
|
||||
```bash
|
||||
/home/claude/bin/gitea-connector/bin/gitea help
|
||||
```
|
||||
|
||||
Expected: The help text prints with all commands listed and silly flavor text.
|
||||
|
||||
- [ ] **Step 4: Test CLI error handling (no config)**
|
||||
|
||||
```bash
|
||||
HOME=/tmp/fakehome /home/claude/bin/gitea-connector/bin/gitea list
|
||||
```
|
||||
|
||||
Expected: Error about missing username, exit code 1.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
git add bin/gitea
|
||||
git commit -m "feat: add gitea bash CLI with silly personality"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Setup Agent
|
||||
|
||||
**Files:**
|
||||
- Create: `gitea-connector/agents/gitea-setup.md`
|
||||
|
||||
- [ ] **Step 1: Write the setup agent**
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: gitea-setup
|
||||
description: Use this agent when a user needs to connect to the Gitea instance at git.sethpc.xyz. Walks them through API key generation, stores credentials, installs the CLI, and validates everything works.
|
||||
model: inherit
|
||||
---
|
||||
|
||||
You are the Gitea Setup Wizard — part helpful guide, part chaos goblin, fully committed to getting this human connected to git.sethpc.xyz without anyone crying.
|
||||
|
||||
Your job: walk the user through connecting their Claude Code environment to Seth's Gitea instance. Be silly but get the job done.
|
||||
|
||||
## Setup Steps
|
||||
|
||||
Follow these steps IN ORDER. Use AskUserQuestion for each input step. Do not skip steps.
|
||||
|
||||
### Step 1: Dependency Check
|
||||
|
||||
Run these commands to verify dependencies:
|
||||
|
||||
```bash
|
||||
command -v curl >/dev/null 2>&1 && echo "curl: OK" || echo "curl: MISSING"
|
||||
command -v jq >/dev/null 2>&1 && echo "jq: OK" || echo "jq: MISSING"
|
||||
command -v git >/dev/null 2>&1 && echo "git: OK" || echo "git: MISSING"
|
||||
```
|
||||
|
||||
If anything is MISSING, tell the user what to install and stop. Be dramatic about it:
|
||||
- "Oh no. You don't have `jq`. That's like showing up to a sword fight with a pool noodle. Install it with `apt install jq` (or your distro's equivalent) and run /gitea-setup again."
|
||||
|
||||
If all OK, proceed with enthusiasm.
|
||||
|
||||
### Step 2: Check for Existing Config
|
||||
|
||||
```bash
|
||||
ls -la ~/.config/gitea/token 2>/dev/null && echo "EXISTS" || echo "NONE"
|
||||
```
|
||||
|
||||
If EXISTS: Ask the user "You've already got Gitea credentials on file. Want to reconfigure? (This won't delete your repos, just your local config. Repos are forever. Well, until someone runs `gitea delete`.)"
|
||||
|
||||
If they say no, stop gracefully: "Smart. If it ain't broke, don't authenticate it."
|
||||
|
||||
### Step 3: Collect Username
|
||||
|
||||
Ask the user: "What's your Gitea username? (This is your login name at git.sethpc.xyz — not your email, not your display name, not your gamer tag.)"
|
||||
|
||||
Store the response for later.
|
||||
|
||||
### Step 4: API Key Generation
|
||||
|
||||
Tell the user exactly how to generate their token. Use this message:
|
||||
|
||||
"Time to forge your API key. Here's the quest:
|
||||
|
||||
1. Open **https://git.sethpc.xyz** in your browser and log in
|
||||
2. Click your **profile picture** (top right corner — yes, that little circle)
|
||||
3. Go to **Settings**
|
||||
4. Click **Applications** in the sidebar
|
||||
5. Under **Manage Access Tokens**, type `claude-code` as the token name
|
||||
6. For permissions, check **repo** (Read and Write) and **user** (Read)
|
||||
7. Click **Generate Token**
|
||||
8. **COPY THE TOKEN NOW** — Gitea only shows it once. It's like a shooting star, except it grants repo access.
|
||||
|
||||
Paste the token here when you've got it."
|
||||
|
||||
Wait for the token via AskUserQuestion.
|
||||
|
||||
### Step 5: Store Credentials
|
||||
|
||||
Run these commands (substitute the actual username and token):
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/gitea
|
||||
echo "USERNAME_HERE" > ~/.config/gitea/username
|
||||
echo "TOKEN_HERE" > ~/.config/gitea/token
|
||||
chmod 600 ~/.config/gitea/username ~/.config/gitea/token
|
||||
```
|
||||
|
||||
Replace USERNAME_HERE and TOKEN_HERE with the actual values collected.
|
||||
|
||||
### Step 6: Install CLI
|
||||
|
||||
Copy the CLI from the plugin to ~/bin/:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/bin
|
||||
cp "${CLAUDE_PLUGIN_ROOT}/bin/gitea" ~/bin/gitea
|
||||
chmod +x ~/bin/gitea
|
||||
```
|
||||
|
||||
Check if ~/bin is on PATH:
|
||||
|
||||
```bash
|
||||
echo "$PATH" | grep -q "$HOME/bin" && echo "ON_PATH" || echo "NOT_ON_PATH"
|
||||
```
|
||||
|
||||
If NOT_ON_PATH, tell the user: "Heads up — `~/bin` isn't on your PATH. Add this to your `~/.bashrc` or `~/.zshrc`:
|
||||
|
||||
```bash
|
||||
export PATH=\"\$HOME/bin:\$PATH\"
|
||||
```
|
||||
|
||||
Then restart your shell or run `source ~/.bashrc`. Otherwise `gitea` will just sit there, lonely and uncallable."
|
||||
|
||||
### Step 7: Validate Token
|
||||
|
||||
Test the token by calling the Gitea API:
|
||||
|
||||
```bash
|
||||
curl -sf "https://git.sethpc.xyz/api/v1/user" \
|
||||
-H "Authorization: token $(cat ~/.config/gitea/token)" | jq -r '.login'
|
||||
```
|
||||
|
||||
Expected: The username they entered in Step 3.
|
||||
|
||||
If the returned username matches: proceed to success.
|
||||
|
||||
If it doesn't match: "Hmm, the token says you're `<returned>` but you told me `<entered>`. One of these is a lie. Which username is correct?" Then update ~/.config/gitea/username with their answer.
|
||||
|
||||
If the request fails entirely: "That token didn't work. Gitea rejected it like a bouncer with standards. Double-check:
|
||||
- Did you copy the whole token? (No leading/trailing spaces?)
|
||||
- Did you actually click Generate? (The token only appears once!)
|
||||
- Can you reach https://git.sethpc.xyz in a browser?
|
||||
|
||||
Generate a new token and try /gitea-setup again."
|
||||
|
||||
### Step 8: Success
|
||||
|
||||
Print the victory message:
|
||||
|
||||
"You're in! Here's what just happened:
|
||||
- Credentials stored in `~/.config/gitea/` (locked down, chmod 600, very secure, much wow)
|
||||
- CLI installed at `~/bin/gitea`
|
||||
- Token validated against git.sethpc.xyz — you are who you say you are
|
||||
|
||||
**Available commands:**
|
||||
```
|
||||
gitea create <name> [--private] [--description "..."]
|
||||
gitea remote <name>
|
||||
gitea push
|
||||
gitea delete <name>
|
||||
gitea list
|
||||
```
|
||||
|
||||
**Quick start:**
|
||||
```bash
|
||||
mkdir my-project && cd my-project && git init
|
||||
gitea create my-project --description "My cool thing"
|
||||
gitea remote my-project
|
||||
# write some code...
|
||||
git add -A && git commit -m "feat: initial commit"
|
||||
gitea push
|
||||
```
|
||||
|
||||
Now go build something. The Gitea server believes in you. I believe in you. Your commits will be legendary."
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
git add agents/gitea-setup.md
|
||||
git commit -m "feat: add interactive gitea-setup agent with guided credential flow"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Setup Command (`/gitea-setup`)
|
||||
|
||||
**Files:**
|
||||
- Create: `gitea-connector/commands/gitea-setup.md`
|
||||
|
||||
- [ ] **Step 1: Write the slash command**
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: gitea-setup
|
||||
description: Connect to Seth's Gitea instance (git.sethpc.xyz) — guided API key setup
|
||||
---
|
||||
|
||||
Launch the gitea-setup agent to walk the user through connecting to the Gitea instance at git.sethpc.xyz.
|
||||
|
||||
This command sets up:
|
||||
1. API token credentials
|
||||
2. The `gitea` CLI tool
|
||||
3. Validates the connection works
|
||||
|
||||
Dispatch the gitea-setup agent to handle this interactively.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
git add commands/gitea-setup.md
|
||||
git commit -m "feat: add /gitea-setup slash command"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Gitea Workflow Skill
|
||||
|
||||
**Files:**
|
||||
- Create: `gitea-connector/skills/gitea-workflow/SKILL.md`
|
||||
|
||||
- [ ] **Step 1: Write the skill**
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: gitea-workflow
|
||||
description: This skill should be used when working in a git repository with a git.sethpc.xyz remote, when the user asks to "commit", "push", "create a repo", "gitea", or when making changes to code that should be committed. Provides commit conventions, CLI reference, and credential safety rules.
|
||||
---
|
||||
|
||||
# Gitea Workflow — Commit Conventions & CLI
|
||||
|
||||
You're working with a repo hosted on Seth's Gitea instance (`git.sethpc.xyz`). Here's how we roll.
|
||||
|
||||
## Commit Conventions (default-on, overridable)
|
||||
|
||||
These conventions apply unless the project's CLAUDE.md says otherwise:
|
||||
|
||||
- **Conventional commits:** Prefix every commit message with a type:
|
||||
- `feat:` — new feature
|
||||
- `fix:` — bug fix
|
||||
- `docs:` — documentation only
|
||||
- `refactor:` — code restructuring, no behavior change
|
||||
- `test:` — adding or updating tests
|
||||
- `chore:` — maintenance, tooling, deps
|
||||
- **Commit immediately** — every meaningful change gets its own commit. Don't hoard changes.
|
||||
- **Always push after commit** — use `gitea push` or `git push`. Code that isn't pushed is just a fancy diary entry.
|
||||
- **No squashing** — every commit is a record. History is sacred (and also useful for debugging).
|
||||
- **No batching unrelated changes** — one commit, one concern. If you changed the auth system AND updated the README, that's two commits.
|
||||
|
||||
## CLI Reference
|
||||
|
||||
The `gitea` CLI handles repo operations against git.sethpc.xyz:
|
||||
|
||||
```
|
||||
gitea create <name> [--private] [--description "..."] # Create a new repo
|
||||
gitea remote <name> # Set/update git origin with token auth
|
||||
gitea push # Push current branch to origin
|
||||
gitea delete <name> # Delete a repo (with confirmation)
|
||||
gitea list # List your repos
|
||||
```
|
||||
|
||||
For creating a new project from scratch:
|
||||
```bash
|
||||
mkdir my-project && cd my-project && git init
|
||||
gitea create my-project --description "What it does"
|
||||
gitea remote my-project
|
||||
# ... write code ...
|
||||
git add -A && git commit -m "feat: initial commit"
|
||||
gitea push
|
||||
```
|
||||
|
||||
## Credential Safety
|
||||
|
||||
**Never commit these:**
|
||||
- `~/.config/gitea/token` or `~/.config/gitea/username`
|
||||
- `.env` files containing secrets
|
||||
- API keys, tokens, or passwords in any form
|
||||
|
||||
**Always ensure `.gitignore` includes:**
|
||||
```
|
||||
.env
|
||||
.env.*
|
||||
*.key
|
||||
*.pem
|
||||
```
|
||||
|
||||
If you see credentials in staged files, warn the user immediately and unstage them.
|
||||
|
||||
## Override Mechanism
|
||||
|
||||
Users can override any convention above in their project's CLAUDE.md. For example, if a project's CLAUDE.md says "squash commits before merge", follow that instead of these defaults. Project-level instructions always win.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
git add skills/gitea-workflow/SKILL.md
|
||||
git commit -m "feat: add gitea-workflow skill with commit conventions and CLI reference"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Push Reminder Hook
|
||||
|
||||
**Files:**
|
||||
- Create: `gitea-connector/hooks/hooks.json`
|
||||
- Create: `gitea-connector/hooks/scripts/push-reminder.sh`
|
||||
|
||||
- [ ] **Step 1: Write the hook script**
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# push-reminder.sh — nudges you to push after a commit
|
||||
# Reads Bash tool output from stdin (JSON), checks if a git commit happened.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
INPUT=$(cat)
|
||||
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
||||
TOOL_RESULT=$(echo "$INPUT" | jq -r '.tool_result // ""')
|
||||
|
||||
# Only trigger on Bash tool
|
||||
[[ "$TOOL_NAME" == "Bash" ]] || exit 0
|
||||
|
||||
# Check if the output looks like a successful git commit
|
||||
if echo "$TOOL_RESULT" | grep -qE '^\[.+\]\s+\S+'; then
|
||||
echo '{"decision":"approve","systemMessage":"Commit detected! Friendly reminder: run `gitea push` to send it to git.sethpc.xyz. Unpushed commits are just really elaborate local notes."}'
|
||||
else
|
||||
echo '{"decision":"approve"}'
|
||||
fi
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Make it executable**
|
||||
|
||||
```bash
|
||||
chmod +x /home/claude/bin/gitea-connector/hooks/scripts/push-reminder.sh
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Write hooks.json (hook disabled by default)**
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Gitea connector hooks — push reminder after commits (opt-in)",
|
||||
"hooks": {}
|
||||
}
|
||||
```
|
||||
|
||||
Note: The hook is shipped disabled. To enable, users update `hooks.json` to:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Gitea connector hooks — push reminder after commits",
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/push-reminder.sh",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Document this in the README (Task 7).
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
git add hooks/hooks.json hooks/scripts/push-reminder.sh
|
||||
git commit -m "feat: add push-reminder hook (opt-in, disabled by default)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: README
|
||||
|
||||
**Files:**
|
||||
- Create: `gitea-connector/README.md`
|
||||
|
||||
- [ ] **Step 1: Write the README**
|
||||
|
||||
```markdown
|
||||
# gitea-connector
|
||||
|
||||
A Claude Code plugin for connecting to Seth's Gitea instance at `git.sethpc.xyz`. Sets up your credentials, installs a CLI, and teaches Claude Code your commit conventions — so you can focus on writing code instead of remembering how to push it.
|
||||
|
||||
## Install
|
||||
|
||||
Add this plugin to your Claude Code setup:
|
||||
|
||||
```bash
|
||||
claude plugin add /path/to/gitea-connector
|
||||
```
|
||||
|
||||
Or clone it:
|
||||
|
||||
```bash
|
||||
git clone https://git.sethpc.xyz/Seth/gitea-connector.git
|
||||
claude plugin add ./gitea-connector
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
Run `/gitea-setup` in Claude Code. It'll walk you through everything:
|
||||
|
||||
1. Checks you have `curl`, `jq`, and `git` (the holy trinity)
|
||||
2. Guides you through generating an API token on git.sethpc.xyz
|
||||
3. Stores your credentials securely in `~/.config/gitea/`
|
||||
4. Installs the `gitea` CLI to `~/bin/`
|
||||
5. Validates your connection
|
||||
|
||||
The whole thing takes about 2 minutes. Less if you type fast.
|
||||
|
||||
## CLI Usage
|
||||
|
||||
After setup, you have the `gitea` command:
|
||||
|
||||
```bash
|
||||
gitea create my-project --private --description "Top secret stuff"
|
||||
gitea remote my-project # wire up git origin
|
||||
gitea push # push current branch
|
||||
gitea list # see your repos
|
||||
gitea delete old-project # delete (with confirmation)
|
||||
```
|
||||
|
||||
## Commit Conventions
|
||||
|
||||
The plugin teaches Claude Code these conventions (override in your project's CLAUDE.md):
|
||||
|
||||
- **Conventional commits:** `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`
|
||||
- **Commit immediately** — don't hoard changes
|
||||
- **Always push** — unpushed commits are just elaborate local notes
|
||||
- **No squashing** — history is sacred
|
||||
- **One concern per commit** — no bundling unrelated changes
|
||||
|
||||
## Push Reminder Hook (opt-in)
|
||||
|
||||
Want a nudge after every commit? Edit `hooks/hooks.json` in the plugin directory:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Gitea connector hooks — push reminder after commits",
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/push-reminder.sh",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Config Files
|
||||
|
||||
All stored in `~/.config/gitea/`:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `token` | Your Gitea API token |
|
||||
| `username` | Your Gitea username |
|
||||
| `host` | (Optional) Host override, defaults to `git.sethpc.xyz` |
|
||||
|
||||
## Requirements
|
||||
|
||||
- `curl`, `jq`, `git`
|
||||
- A Gitea account on `git.sethpc.xyz` (talk to Seth)
|
||||
- Claude Code
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
git add README.md
|
||||
git commit -m "docs: add README with install, setup, and usage instructions"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Create Gitea Repo & Push
|
||||
|
||||
**Files:**
|
||||
- No new files
|
||||
|
||||
- [ ] **Step 1: Create the Gitea repo**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
~/bin/gitea create gitea-connector --description "Claude Code plugin for connecting to Seth's Gitea instance"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Set remote**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
~/bin/gitea remote gitea-connector
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Push all commits**
|
||||
|
||||
```bash
|
||||
cd /home/claude/bin/gitea-connector
|
||||
~/bin/gitea push
|
||||
```
|
||||
|
||||
Expected: All commits pushed to `https://git.sethpc.xyz/Seth/gitea-connector`.
|
||||
@@ -0,0 +1,133 @@
|
||||
# Gitea Connector for Claude Code — Design Spec
|
||||
|
||||
**Date:** 2026-04-01
|
||||
**Status:** Approved
|
||||
**Author:** Seth + Claude
|
||||
|
||||
## Overview
|
||||
|
||||
A Claude Code plugin that lets collaborators connect to Seth's Gitea instance (`git.sethpc.xyz`) with their own accounts. Bundles an adapted bash CLI and provides guided setup, commit conventions, and Gitea-aware skills.
|
||||
|
||||
**Target users:** Friends and collaborators Seth invites, who have their own Gitea accounts on `git.sethpc.xyz` and use Claude Code.
|
||||
|
||||
## Plugin Structure
|
||||
|
||||
```
|
||||
gitea-connector/
|
||||
├── plugin.json # Plugin manifest
|
||||
├── commands/
|
||||
│ └── gitea-setup.md # /gitea-setup slash command
|
||||
├── skills/
|
||||
│ └── gitea-workflow.md # Commit conventions & Gitea awareness
|
||||
├── hooks/
|
||||
│ └── push-reminder.sh # Post-commit push reminder (opt-in)
|
||||
├── agents/
|
||||
│ └── gitea-setup.md # Interactive setup agent
|
||||
├── bin/
|
||||
│ └── gitea # Bash CLI (external-only, no LAN)
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Component Details
|
||||
|
||||
### 1. Bash CLI (`bin/gitea`)
|
||||
|
||||
Adapted from Seth's existing `~/bin/gitea` with these changes:
|
||||
|
||||
- **Host:** Always `https://git.sethpc.xyz` — no LAN detection, no `192.168.0.125` references. All users are external.
|
||||
- **Username:** Read from `~/.config/gitea/username` (not hardcoded to `Seth`).
|
||||
- **Token:** Read from `~/.config/gitea/token` (same convention as original).
|
||||
- **Host override:** Optional `~/.config/gitea/host` file for future flexibility, defaults to `git.sethpc.xyz`.
|
||||
|
||||
Commands (unchanged interface):
|
||||
```
|
||||
gitea create <name> [--private] [--description "..."]
|
||||
gitea remote <name>
|
||||
gitea push
|
||||
gitea delete <name>
|
||||
gitea list
|
||||
```
|
||||
|
||||
Drops the `api_base()` LAN/WAN probe function — replaced with a simple `https://${GITEA_HOST}/api/v1` base URL.
|
||||
|
||||
### 2. Setup Flow (`/gitea-setup`)
|
||||
|
||||
Invokes the `gitea-setup` agent for interactive credential setup.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Dependency check:** Verify `curl`, `jq`, `git` are installed. If missing, tell the user what to install and stop.
|
||||
2. **Existing config check:** If `~/.config/gitea/token` exists, ask if they want to reconfigure.
|
||||
3. **Collect username:** Ask the user for their Gitea username.
|
||||
4. **Walk through API key generation:**
|
||||
> Go to `https://git.sethpc.xyz` → click your **profile icon** (top right) → **Settings** → **Applications** → under **Manage Access Tokens**, name it `claude-code`, grant **repo** (read/write) and **user** (read) permissions, click **Generate Token** → paste the token back here.
|
||||
5. **Store credentials:**
|
||||
- Create `~/.config/gitea/` if missing
|
||||
- Write `token` and `username` files
|
||||
- `chmod 600` both files
|
||||
6. **Install CLI:**
|
||||
- Copy `bin/gitea` to `~/bin/gitea`
|
||||
- `chmod +x ~/bin/gitea`
|
||||
- If `~/bin` not on PATH, warn and suggest adding it
|
||||
7. **Validate:** `GET /api/v1/user` with the token, confirm returned username matches what they entered.
|
||||
8. **Success message:** Confirm everything works, list available commands.
|
||||
|
||||
**Tone:** Silly but appropriate throughout the entire experience — the setup agent's conversational messages, CLI help text, success/error messages, and skill descriptions should all have personality. Think friendly chaos goblin who knows their way around git, not corporate onboarding wizard.
|
||||
|
||||
### 3. Skill (`skills/gitea-workflow.md`)
|
||||
|
||||
Loaded into Claude Code's context when working in repos with a `git.sethpc.xyz` origin. Provides:
|
||||
|
||||
- **Commit conventions (default-on, overridable):**
|
||||
- Conventional commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`
|
||||
- Commit every meaningful change immediately
|
||||
- Always push after commit
|
||||
- No squashing, no batching unrelated changes
|
||||
- **Gitea CLI usage:** Reference for `gitea create`, `remote`, `push`, `delete`, `list`
|
||||
- **Credential safety:** Never commit tokens; ensure `.gitignore` covers `.env`, credential files, `~/.config/gitea/`
|
||||
- **Override mechanism:** Users can override any convention in their project's CLAUDE.md
|
||||
|
||||
Trigger condition: Skill activates when current repo's origin URL contains `git.sethpc.xyz`.
|
||||
|
||||
### 4. Hook (`hooks/push-reminder.sh`)
|
||||
|
||||
- **Type:** PostToolUse hook on the `Bash` tool
|
||||
- **Trigger:** Detects successful `git commit` in command output
|
||||
- **Action:** Echoes a reminder to push
|
||||
- **Default:** Disabled (opt-in via plugin settings)
|
||||
|
||||
### 5. Agent (`agents/gitea-setup.md`)
|
||||
|
||||
The interactive agent behind `/gitea-setup`. Uses `AskUserQuestion` to collect input step by step. Handles all the logic described in the setup flow above.
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Scenario | Behavior |
|
||||
|----------|----------|
|
||||
| Token invalid / expired | "That token didn't work. Generate a new one at Settings → Applications." |
|
||||
| Gitea unreachable | "Can't reach git.sethpc.xyz. Check your network or VPN connection." |
|
||||
| `~/bin` not on PATH | Warn and suggest adding it to shell profile |
|
||||
| Token file exists, re-running setup | "Found existing config. Reconfigure? (y/N)" |
|
||||
| `jq` / `curl` / `git` not installed | Check at start, list what's missing |
|
||||
| Username mismatch (token returns different user) | "Token belongs to <X> but you said <Y>. Which is correct?" |
|
||||
|
||||
## Networking
|
||||
|
||||
- **Always HTTPS to `git.sethpc.xyz`** (Caddy reverse proxy to Gitea CT 146)
|
||||
- No LAN IP references anywhere — all users are external
|
||||
- Default host overridable via `~/.config/gitea/host` file
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `curl` — API calls
|
||||
- `jq` — JSON parsing
|
||||
- `git` — repo operations
|
||||
|
||||
No other external dependencies. Pure bash CLI.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Multi-instance support (connecting to arbitrary Gitea servers)
|
||||
- User account creation on Gitea (Seth invites users manually)
|
||||
- SSH key setup (HTTPS + token auth only)
|
||||
- CI/CD integration
|
||||
Reference in New Issue
Block a user