Obsidian
Portable Agent Skills for AI coding agents (Claude Code, Codex, Cursor, Gemini) — agentskills.io-conformant, versioned, installable via skills.sh
npx -y skills add lx-wnk/skills --skill obsidianAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Obsidian vault access via Local REST API using curl — read, search, create, or update notes in the knowledge base. Trigger on ANY of these: user mentions "obsidian", "vault", "knowledge base", "my notes", or "note" in a save/lookup context; user asks to "save", "persist", "document", "note down", or "look up" information; phrases like "note this down", "add this to my knowledge base", "search my notes", "check if I have notes on X", "create a note"; CLAUDE.md says to search or store in Obsidian before/after tasks — do it proactively; starting a task where prior context might exist → search first; mid-task discovery of a non-obvious insight, gotcha, root cause, or workaround → save it; finishing a task with decisions or learnings worth remembering → persist it; something written to CLAUDE.md or project docs → also save it for cross-project search.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.2 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it
Obsidian Knowledge Base – curl API Reference
When to use this skill
There are three natural moments to reach for Obsidian:
- Before a task — search for existing context so you don't repeat research or contradict prior decisions
- Mid-task — when you discover something non-obvious (a hidden bug cause, a framework gotcha, an architecture constraint, a deliberate tradeoff) that would cost future-you to rediscover
- After a task — persist new learnings, updated project status, or decisions made during the session
If something gets written to CLAUDE.md or project documentation, consider whether it also belongs in Obsidian — CLAUDE.md is project-scoped, Obsidian is cross-project and searchable.
Base Config
- URL:
$OBSIDIAN_BASE_URL - Auth:
$OBSIDIAN_API_KEY - Root folder:
$OBSIDIAN_ROOT(default:claude-memory) - SSL: self-signed cert → always use
curl -sk - Default subfolders:
private/,work/,misc/
Setup: Add these vars to
~/.claude/settings.jsonunder the"env"key — not to~/.claude/settings.local.json(that path is not a recognized Claude Code settings scope and is silently ignored):{ "env": { "OBSIDIAN_BASE_URL": "https://127.0.0.1:27124", "OBSIDIAN_API_KEY": "your-key" } }
Helper alias (reduces repetition)
obs() { curl -sk -H "Authorization: Bearer $OBSIDIAN_API_KEY" "$@"; }
ROOT="${OBSIDIAN_ROOT:-claude-memory}"
Note: Each Bash tool call runs in a fresh shell —
obs()andROOTdo not persist between invocations. Redefine both at the top of each command, or inline the fullcurl -sk -H "Authorization: Bearer $OBSIDIAN_API_KEY"directly.
Search
Uses Obsidian's built-in index — fast even on large vaults.
obs -X POST "$OBSIDIAN_BASE_URL/search/simple/?query=KEYWORD" \
| python3 -c "import sys,json; [print(r['filename']) for r in json.load(sys.stdin)]"
Use specific keywords that likely appear in note filenames (e.g. auth, docker, deployment).
Read a note
obs "$OBSIDIAN_BASE_URL/vault/${ROOT}/work/My-Note.md"
URL-encode spaces as %20. For complex paths:
python3 -c "import urllib.parse; print(urllib.parse.quote('PATH/WITH SPACES'))"
Create or update a note (PUT = upsert)
obs -X PUT \
-H "Content-Type: text/markdown" \
--data-binary "# Title
Content here." \
"$OBSIDIAN_BASE_URL/vault/${ROOT}/work/My-Note.md"
Append to an existing note
obs -X POST \
-H "Content-Type: text/markdown" \
--data-binary "
## New Section
Content." \
"$OBSIDIAN_BASE_URL/vault/${ROOT}/work/My-Note.md"
Delete a note
obs -X DELETE "$OBSIDIAN_BASE_URL/vault/${ROOT}/work/My-Note.md"
List folder contents
obs "$OBSIDIAN_BASE_URL/vault/${ROOT}/work/"
Note naming conventions
- One topic per note — no catch-all files
- Filename = searchable keyword (
Docker-Port-Conflicts.md,Auth-JWT-Setup.md) - Path:
$OBSIDIAN_ROOT/{private|work|misc}/{Topic}.mdwork/— project notes, architecture decisions, tickets, bug findingsprivate/— personal projects and private insightsmisc/— tools, plugins, general knowledge that doesn't fit elsewhere
Error codes
| Code | Meaning |
|---|---|
| 200/204 | OK |
| 401 | Wrong or missing API key |
| 404 | Note/path doesn't exist |
| 0 / refused | Obsidian not running or plugin disabled |
One-time Migration (old → new folder structure)
If your vault still uses a legacy structure (e.g. Claude Code/{Allgemein,Privat,Arbeit}/), run this script to move all notes into the new $OBSIDIAN_ROOT/{misc,private,work}/ layout:
python3 - <<'EOF'
import subprocess, json, urllib.parse, os, sys
BASE = os.environ["OBSIDIAN_BASE_URL"]
KEY = os.environ["OBSIDIAN_API_KEY"]
ROOT = os.environ.get("OBSIDIAN_ROOT", "claude-memory")
FOLDER_MAP = {
"Claude Code/Allgemein": f"{ROOT}/misc",
"Claude Code/Privat": f"{ROOT}/private",
"Claude Code/Arbeit": f"{ROOT}/work",
}
def api(method, path, **kwargs):
url = f"{BASE}/vault/{urllib.parse.quote(path, safe='/')}"
cmd = ["curl", "-sk", "-X", method, "-H", f"Authorization: Bearer {KEY}"]
if "data" in kwargs:
cmd += ["-H", "Content-Type: text/markdown", "--data-binary", kwargs["data"]]
cmd.append(url)
r = subprocess.run(cmd, capture_output=True, text=True)
return r.stdout
for old_folder, new_folder in FOLDER_MAP.items():
listing = api("GET", old_folder + "/")
try:
files = json.loads(listing).get("files", [])
except Exception:
print(f"Skip {old_folder} — not found or empty")
continue
for f in files:
if not f.endswith(".md"):
continue
old_path = f"{old_folder}/{f}"
new_path = f"{new_folder}/{f}"
content = api("GET", old_path)
api("PUT", new_path, data=content)
api("DELETE", old_path)
print(f"Moved: {old_path} → {new_path}")
print("Migration complete.")
EOF