feat: add obsidian-memory skill

Протокол работы Claude с creator/obsidian-vault как долговременной
памятью через Gitea REST API.

Покрывает:
- структуру vault (claude/** — RW, остальное RO)
- frontmatter-конвенции (type, project, tags, created/updated,
  relevance, confidence, private)
- протокол: когда искать, что читать, куда писать, что НЕ делать
- Gitea REST API шпаргалка (read/list/search/create/update/batch)
- gotchas trial-and-error (поле 'content' а не 'content_base64',
  URL-encode путей, sha для PUT)
- формат коммит-сообщений (claude: …)
- правила именования, boundaries, private-флаг
This commit is contained in:
creator
2026-04-19 12:54:54 +00:00
parent 9f3e7f82e7
commit 535f9e8cd0
2 changed files with 246 additions and 0 deletions
+3
View File
@@ -9,6 +9,7 @@ Custom skills for Claude.ai (claude.ai → Settings → Skills).
| **bulletproof** | 12-stage adaptive dev workflow (research → deploy). Adapted for Python/Docker/Traefik/MikroTik/embedded stacks, Gitea CI/CD, SonarQube. Based on Bulletproof v5.0 by Artemiy Miller. |
| **embedded-firmware-engineer** | Bare-metal & RTOS firmware: ESP32/ESP-IDF, STM32 HAL/LL, Nordic nRF, FreeRTOS, Zephyr. NASA/JPL Power of Ten rules, banned functions, DMA/cache coherence, GPIO policy, watchdog strategy, brown-out testing. |
| **my-python-senior** | Senior-level Python engineer for systems, containers, LLM workflows, networking, and file processing. |
| **obsidian-memory** | Protocol for using `creator/obsidian-vault` (Gitea repo) as Claude's long-term memory. Vault layout, frontmatter conventions, Gitea REST API mechanics, write-permission boundaries. |
| **pcb-ai-engineer** | Code-driven schematic & PCB design using Circuit-Synth (Python) → KiCad → Altium. Universal STM-family abstraction with `family → package → pinmap → capabilities` data model. |
## Structure
@@ -34,6 +35,8 @@ claude-skills/
│ ├── files-io.md
│ ├── networking.md
│ └── systems.md
├── obsidian-memory/
│ └── SKILL.md
└── pcb-ai-engineer/
├── SKILL.md
├── main.py
+243
View File
@@ -0,0 +1,243 @@
---
name: obsidian-memory
description: Protocol for using creator/obsidian-vault (Gitea repo at h3fq32.golive.ru) as Claude's long-term memory. Trigger when the user references past work ("my X project", "we decided Y", "remember that..."), asks Claude to remember/save/update/forget something, mentions a project by name (lotus-eletre, uspeak, homework-*, 1c-*, pcb-*, obsidian, claude-skills), or whenever a substantive insight, decision, or fact surfaces that's worth preserving between sessions. Also covers answering non-trivial project questions that benefit from previously stored context. Describes vault layout, frontmatter conventions, Gitea REST API mechanics (read/list/search/create/update), naming rules, and write permissions. Do NOT trigger for simple one-off technical questions or casual chat that doesn't involve the user's ongoing projects or personal context.
---
# obsidian-memory
Протокол работы с `creator/obsidian-vault` как долговременной памятью.
Vault — обычный git-репо в Gitea, хранит markdown-заметки с YAML-frontmatter.
Claude читает и пишет заметки через Gitea REST API.
## Where things live
- **Gitea:** `https://git.h3fq32.golive.ru`
- **Repo:** `creator/obsidian-vault`
- **Branch:** `main`
- **Access:** `bash_tool` + `curl` с `Authorization: token $GITEA_TOKEN`
Токен должен быть в окружении (`$GITEA_TOKEN`) или в сессионном контексте.
Если ни там, ни там его нет — попросить пользователя один раз в начале,
в последующих вызовах использовать.
## Vault layout
```
00-inbox/ user's raw notes, READ-ONLY for Claude
10-projects/ active projects (lotus-eletre/, uspeak/, …), READ-ONLY
20-knowledge/ reference (1c/, embedded/, infrastructure/), READ-ONLY
30-daily/ daily log, meetings, READ-ONLY
claude/ Claude's own space, READ-WRITE
├── memory/
│ ├── facts.md stable facts (one file, append-only section)
│ ├── preferences.md user preferences and standards
│ └── projects/
│ └── <slug>.md cumulative per-project context
├── insights/ dated observations: YYYY-MM-DD-<slug>.md
├── conversations/ session summaries: YYYY-MM-DD-<slug>.md
└── inbox/ drafts for user review
```
**Write rule:** Claude пишет *только* в `claude/**`. Всё остальное —
read-only. Если кажется что стоит дополнить `20-knowledge/…` — положить
в `claude/inbox/` с пометкой «proposed addition to <path>» и отдать
пользователю на ревью.
## Frontmatter
Каждый файл в `claude/**` начинается с YAML-frontmatter:
```yaml
---
type: memory | insight | conversation | fact | preference
project: <slug> # опционально; lotus-eletre, uspeak, и т.п.
tags: [<tag1>, <tag2>] # свободная форма, kebab-case
created: YYYY-MM-DD
updated: YYYY-MM-DD
relevance: 0.0-1.0 # оценка важности, используется при ранжировании
confidence: low | medium | high
private: false # true → не использовать в ответах, не упоминать наличие
sources: # опционально: откуда знание пришло
- conversation: 2026-04-19-lotus-yaw-calibration
- url: https://…
---
```
`private: true` — сильное правило. Такие заметки читать можно
(для понимания контекста пользователя), но в ответах не цитировать,
не ссылаться, и не упоминать факт их существования.
## Protocol
### 1. В начале содержательного разговора
Если разговор касается проекта/системы/темы, по которым у пользователя
может быть накопленный контекст:
1. **Поиск по теме.** Content-поиск по vault через API:
```
GET /api/v1/repos/creator/obsidian-vault/search?q=<keyword>&type=code
```
Альтернатива для поиска по frontmatter-полю — grep по всем файлам в
`claude/memory/projects/`:
```
GET /api/v1/repos/creator/obsidian-vault/contents/claude/memory/projects
```
2. **Читаем top-3..5 самых релевантных.** Приоритет: свежесть (`updated`),
явный `project` match, высокий `relevance`. Исключить `private: true`
из ответов (но прочитать можно).
3. **Использовать контекст** в ответе естественно, без меты про «я
нашёл заметку...».
### 2. Когда НЕ искать
- Простые технические вопросы без персонального контекста
(«как в Python отсортировать список по ключу»)
- Разговорная болтовня, приветствия
- Одноразовые операции
- Вопросы, где пользователь сам дал весь нужный контекст в сообщении
### 3. В процессе разговора
- При упоминании сущностей, имеющих свою заметку в vault, использовать
`[[wiki-links]]` в новых заметках (пригодится для навигации в Obsidian)
- Если обнаружилось противоречие между памятью и тем что говорит
пользователь сейчас — честно сказать («в заметках было X, сейчас Y —
обновить?»), не тихо исправлять
### 4. В конце содержательного разговора
Если всплыли значимые инсайты / факты / решения — сохранить:
| Что появилось | Куда класть |
|---|---|
| Одноразовое наблюдение по проекту | `claude/insights/YYYY-MM-DD-<slug>.md` |
| Накопительный контекст по проекту (обновляется) | `claude/memory/projects/<slug>.md` |
| Стабильный факт общего характера | `claude/memory/facts.md` (append в секцию) |
| Предпочтение пользователя | `claude/memory/preferences.md` |
| Выжимка самой беседы | `claude/conversations/YYYY-MM-DD-<slug>.md` |
| Сомневаешься куда — | `claude/inbox/` + explicit note пользователю |
**Не писать ради галочки.** Лучше 0 файлов, чем пять пустословных.
Критерий: «если через полгода другой instance Claude прочитает эту
заметку — она ему что-то даст?».
## Gitea REST API — шпаргалка
```bash
GITEA=https://git.h3fq32.golive.ru
REPO=creator/obsidian-vault
TOKEN=$GITEA_TOKEN # или подставить вручную
# ── READ ────────────────────────────────────────────────────────────────────
# Прочитать файл (raw содержимое)
curl -sS -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/raw/claude/memory/facts.md"
# Листинг папки (metadata всех файлов)
curl -sS -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/contents/claude/memory/projects?ref=main" \
| python3 -c "import sys,json; [print(x['path'], x['size']) for x in json.load(sys.stdin)]"
# Content-поиск (требует включённый индекс на уровне инстанса Gitea)
curl -sS -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/search?q=eletre+alignment&type=code"
# Tree (вся структура целиком с recursive)
curl -sS -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/git/trees/main?recursive=true"
# ── WRITE ───────────────────────────────────────────────────────────────────
# Создать новый файл (POST contents). Содержимое — base64.
content_b64=$(base64 -w0 /tmp/note.md)
curl -sS -X POST -H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
"$GITEA/api/v1/repos/$REPO/contents/claude/insights/2026-04-19-foo.md" \
-d "{
\"message\": \"claude: insight on X\",
\"content\": \"$content_b64\",
\"branch\": \"main\"
}"
# Обновить существующий файл (PUT, нужен sha текущей версии)
sha=$(curl -sS -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/contents/claude/memory/facts.md?ref=main" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['sha'])")
content_b64=$(base64 -w0 /tmp/facts-new.md)
curl -sS -X PUT -H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
"$GITEA/api/v1/repos/$REPO/contents/claude/memory/facts.md" \
-d "{
\"message\": \"claude: update facts — add Eletre calibration notes\",
\"content\": \"$content_b64\",
\"sha\": \"$sha\",
\"branch\": \"main\"
}"
# Batch (несколько файлов одним коммитом) — POST contents БЕЗ пути
curl -sS -X POST -H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
"$GITEA/api/v1/repos/$REPO/contents" \
-d '{
"branch": "main",
"message": "claude: session summary with 2 artefacts",
"files": [
{"operation":"create", "path":"claude/conversations/…", "content":"<b64>"},
{"operation":"update", "path":"claude/memory/projects/…", "content":"<b64>", "sha":"<prev-sha>"}
]
}'
```
## Gotchas (learned the hard way)
1. **`content`, не `content_base64`.** В `POST /contents` (включая batch)
поле называется `content`. Если послать `content_base64` — Gitea тихо
проигнорирует неизвестный ключ и создаст файл нулевого размера. Файл
«появится», коммит пройдёт, а содержимое будет пустое.
2. **URL-encode путей.** Папки с пробелами/кириллицей → encode перед
API-вызовом: `curl "…/contents/30-daily/2026-04-19%20%D0%B4%D1%8Bнь.md"`.
3. **`PUT` требует `sha` текущей версии.** Забыл sha — получишь 409.
Всегда перечитывать перед update'ом.
4. **Новый репо «пустой» после `auto_init=true`.** Первый commit через
API создаёт initial commit и ветку main. До этого момента API
`?ref=main` возвращает 404.
5. **Rate limit.** По умолчанию Gitea не агрессивен, но batch-операции
предпочтительнее N одиночных запросов — один коммит лучше для истории
и дешевле по HTTP.
## Commit message format
Все коммиты Claude в vault — префикс `claude:`:
- `claude: save <тема> from conversation` — новая заметка
- `claude: update <путь> — <что именно>` — правка
- `claude: merge <откуда> → <куда>` — переорганизация
- `claude: remove <путь> — <причина>` (редко; чаще `private: true`)
Это позволяет пользователю легко фильтровать `git log | grep ^claude:`
и понять что делал Claude в vault.
## Naming
- `claude/insights/YYYY-MM-DD-<slug>.md` — slug kebab-case, ASCII
- `claude/conversations/YYYY-MM-DD-<slug>.md` — аналогично
- `claude/memory/projects/<slug>.md` — slug совпадает с
`10-projects/<slug>/` если проект существует
- `claude/memory/<topic>.md` — accumulating files (facts, preferences)
Русские названия → транслит или короткий англ. эквивалент.
`лотос-настройка-развала` → `lotus-alignment`.
## Boundaries
- **Не писать в пользовательские папки** (00–30). Если нужно предложить
изменение — в `claude/inbox/` с пометкой.
- **Не удалять пользовательские файлы никогда.** Свои (`claude/**`) —
можно, если очевидно устарело; предпочтительнее `private: true` чем
удаление (история сохраняется в git, но из выдачи исчезает).
- **Не обходить `private: true`.** Такие заметки не цитировать
и не упоминать.
- **Честно флагить противоречия** между тем что в vault и что говорит
пользователь, не исправляя молча.