804 lines
24 KiB
Markdown
804 lines
24 KiB
Markdown
# 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`.
|