--- name: gitea description: "Push Hermes skill updates to the local Gitea instance at 10.0.0.61:3000. Clone, copy, commit, push workflow." version: 1.3.0 author: Hermes Agent license: MIT platforms: [linux] metadata: hermes: tags: [gitea, git, skills, push, version-control] --- # Gitea — Local Git Hosting for Hermes Skills Push skill updates to the local Gitea instance at `http://10.0.0.61:3000`. ## Quick Reference | Setting | Value | |---------|-------| | URL | `http://10.0.0.61:3000` | | Repo | `SpeedyFoxAi/hermes-skills` | | Clone URL | `http://10.0.0.61:3000/SpeedyFoxAi/hermes-skills.git` | | Auth URL | `http://TOKEN@10.0.0.61:3000/SpeedyFoxAi/hermes-skills.git` | | Token | `2c6cacb89b8124a98a52206fdbee51cefcd46844` | | Local clone | `/tmp/hermes-skills` | | Skill source | `~/.hermes/profiles//skills/` (any profile — general, research, dev, etc.) | ## Versioning Every skill in the repo MUST have a `version:` field in its YAML frontmatter. Before pushing, always check and bump the version: | Change type | Bump | Example | |---|---|---| | Fixes, clarifications, pitfall additions | patch | 1.0.0 → 1.0.1 | | New features, new sections, new references | minor | 1.0.0 → 1.1.0 | | Rewrites, major restructuring | major | 1.0.0 → 2.0.0 | ## Workflow ### Push Updated Skills ```bash # 1. Clone (or pull if already cloned) cd /tmp && git clone http://10.0.0.61:3000/SpeedyFoxAi/hermes-skills.git # Or: cd /tmp/hermes-skills && git pull origin main # 2. Copy updated skill files from the active profile cp ~/.hermes/profiles/general/skills///SKILL.md /tmp/hermes-skills//SKILL.md # 3. Bump the version in the copied SKILL.md frontmatter # Edit /tmp/hermes-skills//SKILL.md and increment version: # - patch: fixes, clarifications, pitfall additions # - minor: new features, new sections, new references # - major: rewrites, major restructuring # 4. Commit with version in message cd /tmp/hermes-skills git add /SKILL.md git commit -m ": v" # 5. Push with token git remote set-url origin http://2c6cacb89b8124a98a52206fdbee51cefcd46844@10.0.0.61:3000/SpeedyFoxAi/hermes-skills.git git push origin main ``` ### Add a New Skill to the Repo ```bash cd /tmp/hermes-skills mkdir cp ~/.hermes/profiles/general/skills///SKILL.md /SKILL.md # Verify the SKILL.md has a version: field in its frontmatter. # New skills should start at 1.0.0. git add / git commit -m "Add v1.0.0" git push origin main ``` ## Pre-Push Token Validation (MANDATORY — before every push) Tokens expire, get rotated, or are revoked. Always validate the token BEFORE attempting a push: ```bash # Test token validity — 200 = valid, 401 = stale/rotated curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: token " \ http://10.0.0.61:3000/api/v1/user ``` If 401, stop and ask the user for a new token. Do NOT attempt the push — it will fail and the error messages are misleading ("could not read Password", "terminal prompts disabled", "Authentication failed"). ## Pitfalls - **Token in URL is required for push.** The repo is public-read but write-protected. Always set the remote URL with the token before pushing: `git remote set-url origin http://TOKEN@10.0.0.61:3000/SpeedyFoxAi/hermes-skills.git` - **Token-in-URL may fail with "could not read Password".** Some git configurations ignore the password in the URL and still prompt. If `git push http://TOKEN@host/repo.git` fails, try `git -c credential.helper= -c credential.helper='!f() { echo "username=token"; echo "password=TOKEN"; }; f' push origin main`. If that also fails, the token itself is likely stale — validate it first with the curl check above. - **Hardcoded token in this skill is a convenience, not a guarantee.** The token `2c6cacb89b8124a98a52206fdbee51cef46844` was valid at skill creation time but may have been rotated since. Always run the pre-push curl validation before relying on it. - **Git identity must be set.** The clone in `/tmp` won't have user.email/user.name configured. Set them before committing: `git config user.email "n8n@hermes-main.local" && git config user.name "Hermes Agent"` - **Skill directory names in the repo are flat** — e.g. `ask-claude/SKILL.md`, not `autonomous-ai-agents/ask-claude/SKILL.md`. The category hierarchy exists in `~/.hermes/profiles/general/skills/` but the Gitea repo uses flat skill names. - **Clone is disposable.** `/tmp/hermes-skills` is a working copy. Don't store anything there you need to keep. Re-clone if the directory is missing or stale. - **No credential helper needed.** The token-in-URL pattern works for one-shot pushes. Don't store the token in git config or credential files. - **Cross-profile pushes require `cross_profile=True`.** When pushing skills from the research profile (or any non-general profile), the write_file guard will block. Pass `cross_profile=True` to bypass. The Gitea repo is the canonical store for ALL profiles' skills, not just general. - **New skills need a `version:` field.** If a skill is missing `version:` in its YAML frontmatter, add `version: 1.0.0` before pushing. The Gitea repo requires it. ## Sync Audit (Check All Skills Are Versioned and in Gitea) Use this Python script to cross-reference all local skills against Gitea: ```python import os, re skills_dir = "/home/n8n/.hermes/profiles/general/skills" skills = [] for root, dirs, files in os.walk(skills_dir): if "SKILL.md" in files: path = os.path.join(root, "SKILL.md") content = open(path).read() name = re.search(r'^name:\s*(.+)$', content, re.MULTILINE) version = re.search(r'^version:\s*(.+)$', content, re.MULTILINE) name = name.group(1).strip() if name else os.path.basename(root) version = version.group(1).strip() if version else "unknown" skills.append((name, version, path)) gitea_dir = "/tmp/hermes-skills" gitea_skills = set() gitea_versions = {} for entry in os.listdir(gitea_dir): skill_path = os.path.join(gitea_dir, entry, "SKILL.md") if os.path.isfile(skill_path): content = open(skill_path).read() gname = re.search(r'^name:\s*(.+)$', content, re.MULTILINE) gversion = re.search(r'^version:\s*(.+)$', content, re.MULTILINE) gname = gname.group(1).strip() if gname else entry gversion = gversion.group(1).strip() if gversion else "unknown" gitea_skills.add(gname) gitea_versions[gname] = gversion for name, version, path in sorted(skills): in_gitea = "✓" if name in gitea_skills else "✗ MISSING" gitea_ver = gitea_versions.get(name, "—") ver_match = "✓" if version == gitea_ver else f"✗ (local:{version} vs gitea:{gitea_ver})" print(f" {name:40s} v{version:10s} gitea:{in_gitea:12s} version:{ver_match}") ``` Run this after any batch of skill changes. Fix issues: - **Missing `version:`** → add `version: 1.0.0` to the skill's YAML frontmatter - **Missing from Gitea** → `mkdir` the directory and copy the SKILL.md - **Version mismatch** → copy the updated local SKILL.md to Gitea