Files
git.sethpc.xyz-connector/docs/superpowers/plans/2026-04-01-gitea-connector.md
T
2026-04-01 17:51:39 -04:00

24 KiB

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

mkdir -p /home/claude/bin/gitea-connector/.claude-plugin
  • Step 2: Write plugin.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
mkdir -p /home/claude/bin/gitea-connector/{commands,agents,skills/gitea-workflow,hooks/scripts,bin}
  • Step 4: Init git repo
cd /home/claude/bin/gitea-connector && git init
  • Step 5: Create .gitignore
.backup/
*.swp
*.swo
  • Step 6: Commit scaffold
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.

#!/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
chmod +x /home/claude/bin/gitea-connector/bin/gitea
  • Step 3: Test CLI help output
/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)
HOME=/tmp/fakehome /home/claude/bin/gitea-connector/bin/gitea list

Expected: Error about missing username, exit code 1.

  • Step 5: Commit
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

---
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

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):

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/:

mkdir -p ~/bin
cp "${CLAUDE_PLUGIN_ROOT}/bin/gitea" ~/bin/gitea
chmod +x ~/bin/gitea

Check if ~/bin is on PATH:

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:

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:

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:

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

---
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
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

---
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 [--private] [--description "..."] # Create a new repo gitea remote # Set/update git origin with token auth gitea push # Push current branch to origin gitea delete # 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

#!/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
chmod +x /home/claude/bin/gitea-connector/hooks/scripts/push-reminder.sh
  • Step 3: Write hooks.json (hook disabled by default)
{
  "description": "Gitea connector hooks — push reminder after commits (opt-in)",
  "hooks": {}
}

Note: The hook is shipped disabled. To enable, users update hooks.json to:

{
  "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
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

# 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:

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:

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:

{
  "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

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
cd /home/claude/bin/gitea-connector
~/bin/gitea remote gitea-connector
  • Step 3: Push all commits
cd /home/claude/bin/gitea-connector
~/bin/gitea push

Expected: All commits pushed to https://git.sethpc.xyz/Seth/gitea-connector.