feat: add home-assistant-dashboard-conventions skill + update multiple skills

- New: smart-home/home-assistant-dashboard-conventions (Mushroom cards, view tabs, no Bubble Cards)
- Updated: rke2, ceph, galera, proxmox, brainstorming, compound-learning, 1password-cli, smart-home-automation skills
- New references: ceph-cluster-administration, docker-volume-forensics, ceph-crush-weight, ceph-ec-mixed-size
This commit is contained in:
Debian
2026-07-14 18:35:16 +00:00
parent e9cc106625
commit 01bd921ced
37 changed files with 6133 additions and 126 deletions
@@ -0,0 +1,294 @@
# Gitea API, Project Boards & Skills Versioning
## Link Format Convention (USER PREFERENCE)
**Always use `git.familie-schoen.com` for user-facing links in chat responses.**
Never paste the internal IP `10.0.30.105:3000` as a clickable link to the user.
| Context | Format | Example |
|---------|--------|---------|
| User-facing links in chat | `git.familie-schoen.com` | `http://git.familie-schoen.com/dominik/iac-homelab/issues/14` |
| API calls (curl) | `10.0.30.105:3000` | `curl http://10.0.30.105:3000/api/v1/repos/...` |
| SSH to Gitea host | proxmox6 IP `10.0.20.60` | `ssh root@10.0.20.60 'pct exec 108 -- ...'` |
The domain `git.familie-schoen.com` resolves externally; the internal IP is
needed for API calls from within the network. When referencing Gitea issues,
PRs, or boards in any user-visible output, always use the domain form.
## Gitea Server Access
- **Internal URL (API only)**: http://10.0.30.105:3000 (CT108 on proxmox6)
- **External URL (user-facing links)**: http://git.familie-schoen.com
- **User**: dominik
- **Token**: Generate via CLI on CT108 (1Password `op` may fail on VM200):
```bash
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.30.105 \
"su -s /bin/sh gitea -c 'gitea admin user generate-access-token -u dominik -t <name> --scopes all --raw --config /etc/gitea/app.ini'"
```
- **Existing repos**: dominik/iac-homelab (IaC), dominik/hermes-skills (skills)
## Gitea API Patterns
```bash
TOKEN="<generated-token>"
BASE="http://10.0.30.105:3000/api/v1"
REPO="dominik/iac-homelab"
# Create issue
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d '{"title":"Issue title","body":"Description","labels":[1,5],"assignees":["dominik"]}' \
"$BASE/repos/$REPO/issues"
# Comment on issue
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d '{"body":"Comment text"}' \
"$BASE/repos/$REPO/issues/ISSUE_NUMBER/comments"
# List all issues
curl -s -H "Authorization: token $TOKEN" "$BASE/repos/$REPO/issues?limit=50&type=issues"
# Close issue
curl -s -X PATCH -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d '{"state":"closed"}' "$BASE/repos/$REPO/issues/ISSUE_NUMBER"
# Create labels (returns ID needed for issue creation)
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"priority:high","color":"#b60205"}' "$BASE/repos/$REPO/labels"
```
## Pitfall: Gitea 1.25.5 Project Board API Missing
Gitea 1.25.5 (dev build `go1.25.8, bindata/sqlite`) has project tables in DB
and repo unit enabled, but **REST API routes for projects are not registered**.
All project endpoints return 404:
- `/repos/{owner}/{repo}/projects`
- `/repos/{owner}/{repo}/projects/` (trailing slash doesn't help)
- Org-level (`/orgs/{org}/projects`) and user-level project endpoints
### Accessing CT108 (Gitea)
CT108 runs on **proxmox6** (10.0.20.60), NOT directly reachable at 10.0.30.105
for SSH. The 10.0.30.105 address is the CT's network IP for HTTP/API only.
```bash
# CORRECT: SSH to proxmox6, then pct exec
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.20.60 'pct exec 108 -- bash -c "..."'
# WRONG: SSH to 10.0.30.105 (this is the CT's DHCP IP, not a hypervisor)
# ssh root@10.0.30.105 → fails or goes to wrong host
```
### Generating a Gitea Token
The `gitea` binary is at `/usr/local/bin/gitea` — NOT in the default PATH
for the `gitea` system user. Must use the full path:
```bash
# CORRECT (note full binary path):
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.20.60 'pct exec 108 -- bash -c "
su -s /bin/sh gitea -c \"/usr/local/bin/gitea admin user generate-access-token -u dominik -t <name> --scopes all --raw --config /etc/gitea/app.ini\"
"'
# WRONG (gitea not in PATH):
# su -s /bin/sh gitea -c 'gitea admin ...' → "gitea: not found" (exit 127)
```
### Workaround: Direct SQLite manipulation
```bash
# SSH to proxmox6, then pct exec into CT108
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.20.60
# Enter CT108
pct exec 108 -- bash
# Create project board directly in SQLite
sqlite3 /var/lib/gitea/data/gitea.db \
"INSERT INTO project (title, description, repo_id, type, board_type, card_type, creator_id, is_closed, created_unix, updated_unix) \
VALUES ('Board Title', 'Description', REPO_ID, 2, 0, 0, 1, 0, strftime('%s','now'), strftime('%s','now'));"
# type=2 for repo-level project, board_type=0 for kanban
# Get the inserted project ID: SELECT last_insert_rowid();
# Create board columns (Backlog, Todo, In Progress, Done)
sqlite3 /var/lib/gitea/data/gitea.db << 'SQL'
INSERT INTO project_board (title, sorting, project_id, creator_id, created_unix, updated_unix) VALUES
('Backlog', 0, PROJECT_ID, 1, strftime('%s','now'), strftime('%s','now')),
('Todo', 1, PROJECT_ID, 1, strftime('%s','now'), strftime('%s','now')),
('In Progress', 2, PROJECT_ID, 1, strftime('%s','now'), strftime('%s','now')),
('Done', 3, PROJECT_ID, 1, strftime('%s','now'), strftime('%s','now'));
SQL
# Map issues to board columns
sqlite3 /var/lib/gitea/data/gitea.db << 'SQL'
INSERT INTO project_issue (issue_id, project_id, project_board_id, sorting) VALUES
(ISSUE_DB_ID, PROJECT_ID, BOARD_COLUMN_ID, SORT_ORDER);
SQL
```
### ⚠️ SQLite Schema Quirks
1. **`index` is a SQLite reserved word** — the `issue` table has a column
named `index` (the issue number displayed in UI). Must always quote it:
```sql
SELECT id, "index", name FROM issue WHERE repo_id=34; -- CORRECT
SELECT id, index, name FROM issue WHERE repo_id=34; -- SYNTAX ERROR
```
2. **`project_issue` table has NO timestamp columns** — unlike most Gitea
tables, it only has: `id`, `issue_id`, `project_id`, `project_board_id`,
`sorting`. Do NOT include `created_unix`/`updated_unix` in INSERTs.
3. **Issue `id` ≠ `index`** — the API returns `number` (which maps to
`index` in DB), but `project_issue` references the DB primary key `id`.
Always look up both: `SELECT id, "index", name FROM issue WHERE repo_id=N;`
### Label Color Format
Gitea API rejects colors with `#` prefix. Use bare hex:
```bash
# CORRECT:
curl -d '{"name":"ceph","color":"7050ff"}' # → 200 OK
# WRONG:
curl -d '{"name":"ceph","color":"#7050ff"}' # → 404 / error
```
### Label IDs are numeric in API
When creating issues via API, the `labels` field takes **numeric IDs** (from
label creation response), not label names. Passing names silently ignores them.
Look up IDs first: `GET /repos/{owner}/{repo}/labels` → use `id` field.
### Complete PM Setup Recipe (End-to-End)
```bash
TOKEN="<generated-token>"
BASE="http://10.0.30.105:3000/api/v1"
AUTH="Authorization: token $TOKEN"
# 1. Create org
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
"$BASE/orgs" -d '{"username":"pm-infra","description":"...","visibility":"private"}'
# 2. Create repo under org
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
"$BASE/orgs/pm-infra/repos" \
-d '{"name":"homelab-board","private":true,"has_issues":true,"has_projects":true,"default_branch":"main"}'
# 3. Create labels (colors WITHOUT #)
for label in "ceph:7050ff:Ceph storage" "k8s:326ce5:Kubernetes" ...; do
IFS=: read name color desc <<< "$label"
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
"$BASE/repos/pm-infra/homelab-board/labels" \
-d "{\"name\":\"$name\",\"color\":\"$color\",\"description\":\"$desc\"}"
done
# 4. Create milestones
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
"$BASE/repos/pm-infra/homelab-board/milestones" \
-d '{"title":"Infra Audit & Cleanup","description":"..."}'
# 5. Create issues (labels=numeric IDs, milestone=numeric ID)
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
"$BASE/repos/pm-infra/homelab-board/issues" \
-d '{"title":"...","body":"...","labels":[8,17],"milestone":1}'
# 6. Close done issues
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
"$BASE/repos/pm-infra/homelab-board/issues/3" -d '{"state":"closed"}'
# 7. Create project board via SQLite (API returns 404)
# → See "Direct SQLite manipulation" section above
# 8. Map issues to board columns via SQLite
# → See "SQLite Schema Quirks" for gotchas
```
### ⚠️ execute_code Blocked by Cron Approval Mode
`execute_code` may be blocked with "cron approval mode" error even in
interactive sessions. When it fails, use `terminal` + `curl` + `python3 -c`
instead. Avoid `execute_code` for Gitea API scripting — use inline Python
in terminal commands for JSON construction.
## Skills Versioning + Gitea Sync (2026-07-12)
### Setup
```bash
cd ~/.hermes/skills/
# .gitignore (exclude archives + bundled manifest)
cat > .gitignore << 'EOF'
.archive/
.bundled_manifest
*.pyc
__pycache__/
EOF
# Init + commit + push
git init
git add -A
git commit -m "Initial commit: Hermes Agent Skills collection"
git branch -M main # rename master → main
git remote add origin http://10.0.30.105:3000/dominik/hermes-skills.git
git push -u "http://dominik:<token>@10.0.30.105:3000/dominik/hermes-skills.git" main
# Clean token from stored remote URL
git remote set-url origin http://10.0.30.105:3000/dominik/hermes-skills.git
```
### Result
- 789 files, 233,126 lines committed
- Repo: http://10.0.30.105:3000/dominik/hermes-skills
- Private repo, owned by dominik
### Pitfalls
1. **Default branch is `master`** — `git push main` fails with "src refspec main does not match any". Fix: `git branch -M main` before pushing.
2. **Token in remote URL** — `git push` needs auth. Embed token in push URL: `http://dominik:<token>@host/...`. After successful push, reset URL without token: `git remote set-url origin http://host/...` to avoid storing credentials in `.git/config`.
3. **Submodule warnings** — Some skill directories (e.g. `productivity/noris-pptx`) may be git submodules. `git add -A` warns but still commits them as regular files. Check `git rm --cached` if needed.
4. **1Password CLI may fail on VM200** — `op item get "Gitea Token" --vault "Kubernetes ESO" --reveal` fails intermittently. Fall back to generating a fresh token via `gitea admin user generate-access-token` on CT108 directly.
## PM Tool Decision: Gitea over Telegram Topics (2026-07-12)
User explicitly chose **Gitea Issues + Project Boards** over Telegram
Topics as the PM tool for infrastructure work. The Telegram Topics
approach (below) was abandoned because:
- Bots cannot create supergroups (requires manual user action)
- Telegram topics lack structured state management (labels, milestones,
assignees, dependencies)
- Gitea integrates with the existing IaC repo workflow
The complete Gitea PM setup (org → repo → labels → milestones → issues
→ project board via SQLite) is documented in the sections above and was
fully implemented on 2026-07-12. The board lives at:
`http://10.0.30.105:3000/pm-infra/homelab-board`
## Telegram Topics as PM Tool (ABANDONED — kept for reference)
### Limitation: Bots cannot create supergroups
Telegram Bot API does not allow bots to create groups or supergroups. The user
must manually:
1. Create a new group in Telegram
2. Add `@hermesdominik_bot` as **Admin** (with "Add New Members" + "Manage Topics" rights)
3. Enable **Topics** in group settings ("Topics" toggle)
4. Share the Chat ID with the agent
Once the bot is admin in a topics-enabled supergroup, the agent can create
topic threads via the Bot API:
```bash
# Create a topic thread (requires forum_topic icon color)
curl -s -X POST "https://api.telegram.org/bot<TOKEN>/createForumTopic" \
-d "chat_id=-100xxxx&name=Ceph Crisis&icon_color=0xff6b6b"
# Send message to a specific topic
curl -s -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
-d "chat_id=-100xxxx&message_thread_id=TOPIC_ID&text=Status update"
```
### Bot Token Location
- Stored in `~/.hermes/.env` as `TELEGRAM_BOT_TOKEN`
- Source with: `source ~/.hermes/.env && export TELEGRAM_BOT_TOKEN`
- Bot: `@hermesdominik_bot` (ID: 8482479728)
- Home chat: 223926918 (DM, not a group)