agentsclimarketplace

Obsidian vault add

Skill kogakure/skills/obsidian/obsidian-vault-add

Collection of AI Agent Code skills

Install
npx -y skills add kogakure/skills --skill obsidian-vault-add

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 8 stars8 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

Add a new book, podcast, TV show, movie, person, country, or city to the Obsidian Zettelkasten vault. Creates the resource note from a template, fills in metadata from the web, downloads cover art, updates today's daily note (media only), and re-indexes qmd. Use when the user says "add book", "add podcast", "add show", "add movie", "add person", "add country", "add city", or wants to log media, people, or places in their vault.

SKILL.md

13.2 KB, as published. Nobody here has run it

obsidian-vault-add

Add a new resource (book, podcast, TV show, movie, person, country, or city) to the vault: create the note, download a photo/cover, update today's daily note (media only), and re-index.

Argument parsing

Arguments: <type> <title> [extra info]

  • type (required): book, podcast, show (or tv), movie, person, country (or land), city (or stadt)
  • title (required): the resource title
  • extra info (optional): author, host, episode number, season/episode, country name (for cities), etc.

Examples:

  • /obsidian-vault-add book "Atlas Shrugged"
  • /obsidian-vault-add podcast "Aethervox Ehrenfeld"
  • /obsidian-vault-add show "Silicon Valley" 1.1
  • /obsidian-vault-add movie "Dune"
  • /obsidian-vault-add person "Thomas Sowell"
  • /obsidian-vault-add country "Japan"
  • /obsidian-vault-add city "Kyoto"

If type or title is missing, ask the user before proceeding.

Step-by-step workflow

1. Web search for metadata

Search for the title to gather:

Book: title, subtitle, authors, published year, genres, pages, ISBN, short description (German preferred) Podcast: full name, host(s), language, genres, start year, description (German preferred) TV show: full name, actors, genres, network, seasons, episodes, IMDb rating, description (German preferred) Movie: full name, director(s), actors, genres, year, IMDb rating, description (German preferred) Person: full name, aliases, nationality, birthday (and death if applicable), domain/occupation, Wikipedia URL, notable works (books/articles/videos), short German biography (2–4 sentences) Country: official name, capital, continent, official language(s), currency, aliases (native name, abbreviations), short description (German preferred) City: full name, country, region/state, language(s), aliases (native name), short description (German preferred)

2. Derive filename

Colons are not allowed in filenames. If the title contains a colon (e.g. "Book: Subtitle"):

  1. Replace the colon (and any surrounding spaces) with - → filename becomes Book - Subtitle.md
  2. Add the original title with the colon as the first alias in the frontmatter aliases list

3. Check if note already exists

obsidian read file="<derived-filename>" 2>/dev/null | grep -Ev "^(20[0-9]{2}-|Your Obsidian)"

If the note already exists, inform the user and stop (unless they want to update it).

4. Create note from template

TypePathTemplate
book03 Resources/Bücher/<title>.mdBook Template
podcast03 Resources/Podcasts/<title>.mdPodcast Template
show03 Resources/Serien/<title>.mdTV Show Template
movie03 Resources/Filme/<title>.mdMovie Template
person03 Resources/Personen/<title>.mdPerson Template
country03 Resources/Länder/<title>.mdCountry
city03 Resources/Städte/<title>.mdCity
obsidian create path="<path>" template="<Template>" 2>/dev/null | grep -Ev "^(20[0-9]{2}-|Your Obsidian)"

5. Fill in frontmatter

Edit the note file directly (Read then Edit) — do NOT use obsidian property:set for list fields as it does not reliably handle arrays. Set all fields in one Edit call by replacing the entire frontmatter block.

IMPORTANT — preserve existing data: When updating an existing note, never discard data already present. Merge new metadata with what's there:

  • Keep all existing aliases and append any new ones not already listed
  • Keep all existing tags (only remove BOAT if present)
  • Keep any fields already filled in; only add/update empty fields
  • Keep all existing body content (sections, lists, notes)

Book frontmatter fields: aliases, tags: [Buch], photo, urls, published (YYYY-MM-DD), authors (list), genres (list), rating, pages

Podcast frontmatter fields: aliases, tags: [Podcast], photo, urls, hosts (list), guests, genres (list), language, rating, status, started, ended, episodes

TV show frontmatter fields: aliases, tags: [Serie], photo, urls, actors (list), genres (list), rating, imdb_rating, network, seasons, episodes

Movie frontmatter fields: aliases, tags: [Film], photo, urls, directors (list), actors (list), genres (list), rating, imdb_rating, year

Country frontmatter fields: aliases, tags: [Land], photo, urls, capital, continent, language (list), currency, visited, rating, cities (list of wikilinks to known city notes)

Person frontmatter fields: aliases (other names, pen names, maiden names), tags (Mann/Frau, nationality country, domain tags like Schriftsteller/Philosoph/etc.), photo, urls, related, birthday (YYYY-MM-DD), death (YYYY-MM-DD or empty), location, quotes (list of wikilinks), books (list of wikilinks to vault book notes), articles, podcasts, videos

City frontmatter fields: aliases, tags: [Stadt], photo, urls, country (wikilink), region, language (list), visited, rating

YAML quoting rule: Any frontmatter value that contains a colon must be wrapped in double quotes — including titles, descriptions, aliases, and all string fields. Example: title: "Book: Subtitle", aliases: ["Book: Subtitle"].

Remove the BOAT tag from tags — notes with links are not BOAT.

6. Add description to body

After the frontmatter, add a short German-language description paragraph (2–4 sentences). Keep any template section headers (## Zusammenfassung, ## Kernaussagen, ## Lieblingsfolgen, etc.).

Person body structure: short German bio paragraph, then ### Fakten (bullet list of key facts), then ### Ideen (empty, for future notes).

The image embed ![[filename.webp]] will be added after cover download (step 6) — do not add it manually.

7. Download cover art

Use inline Python (not the download scripts, which require manual list editing). Use Pillow if available for WebP output, otherwise JPEG.

Book — Open Library by title search:

import json, urllib.request, urllib.parse, io, os

query = "<title>"
url = f"https://openlibrary.org/search.json?title={urllib.parse.quote(query)}&limit=3"
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 VaultBot/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
    data = json.loads(resp.read())
docs = data.get("docs", [])
cover_id = next((d["cover_i"] for d in docs if d.get("cover_i")), None)
if cover_id:
    img_url = f"https://covers.openlibrary.org/b/id/{cover_id}-L.jpg"
    # download and save as WebP using Pillow

Output path: 99 Meta/assets/books/<title> (<first author>).webp

Podcast — iTunes Search API:

import json, urllib.request, urllib.parse

query = "<podcast name>"
params = urllib.parse.urlencode({"term": query, "entity": "podcast", "limit": 5})
url = f"https://itunes.apple.com/search?{params}"
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 VaultBot/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
    data = json.loads(resp.read())
artwork = data["results"][0].get("artworkUrl600") if data["results"] else None
# download and save as WebP using Pillow

Output path: 99 Meta/assets/podcasts/<title>.webp

TV show — TVMaze API:

import json, urllib.request, urllib.parse

query = "<show title>"
url = f"https://api.tvmaze.com/singlesearch/shows?q={urllib.parse.quote(query)}&embed=images"
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 VaultBot/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
    data = json.loads(resp.read())
image_url = data.get("image", {}).get("original") or data.get("image", {}).get("medium")
# download and save as WebP using Pillow

Output path: 99 Meta/assets/series/<title>.webp

Movie — Wikipedia REST API (search for poster):

import json, urllib.request, urllib.parse

title = "<movie title>"
search_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{urllib.parse.quote(title)}"
req = urllib.request.Request(search_url, headers={"User-Agent": "Mozilla/5.0 VaultBot/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
    data = json.loads(resp.read())
image_url = data.get("originalimage", {}).get("source") or data.get("thumbnail", {}).get("source")
# download and save as WebP using Pillow

Output path: 99 Meta/assets/movies/<title>.webp

Country — Wikipedia REST API (flag or representative image):

import json, urllib.request, urllib.parse

title = "<country name>"
search_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{urllib.parse.quote(title)}"
req = urllib.request.Request(search_url, headers={"User-Agent": "Mozilla/5.0 VaultBot/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
    data = json.loads(resp.read())
image_url = data.get("originalimage", {}).get("source") or data.get("thumbnail", {}).get("source")
# download and save as WebP using Pillow

Output path: 99 Meta/assets/countries/<title>.webp

Person — Wikipedia REST API, then resize to max 800px on longest side:

import json, urllib.request, urllib.parse

name = "<person name>"
search_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{urllib.parse.quote(name)}"
req = urllib.request.Request(search_url, headers={"User-Agent": "Mozilla/5.0 VaultBot/1.0"})
with urllib.request.urlopen(req, timeout=15) as resp:
    data = json.loads(resp.read())
image_url = data.get("originalimage", {}).get("source") or data.get("thumbnail", {}).get("source")
# download, resize to max 800px longest side, save as WebP using Pillow

Output path: 99 Meta/assets/people/<title>.webp

City — Wikipedia REST API:

import json, urllib.request, urllib.parse

title = "<city name>"
search_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{urllib.parse.quote(title)}"
req = urllib.request.Request(search_url, headers={"User-Agent": "Mozilla/5.0 VaultBot/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
    data = json.loads(resp.read())
image_url = data.get("originalimage", {}).get("source") or data.get("thumbnail", {}).get("source")
# download and save as WebP using Pillow

Output path: 99 Meta/assets/cities/<title>.webp

After successful download:

  1. Set photo: <filename> in frontmatter (Edit the file)
  2. Add ![[<filename>]] to the note body using Edit (insert before the description paragraph) — do NOT use obsidian prepend, as it escapes the ! and produces \![[...]]

If cover download fails, note it to the user and continue without a photo.

Also add the new entry to the appropriate download script so future re-runs include it:

TypeScript
book99 Meta/scripts/download-book-covers.py
podcast99 Meta/scripts/download-podcast-covers.py
show99 Meta/scripts/download-series-covers.py
movie99 Meta/scripts/download-movie-covers.py

Countries, cities, and persons have no dedicated download script — skip this step for those types.

8. Update today's daily note

Skip this step for person, country, and city — they are reference notes, not media being consumed.

For media types, append to today's daily note using the correct emoji and wikilink format:

TypeFormat
book📚 [[<title>]] angefangen zu lesen (0 %)
podcast🎙️ [[<title>]]: <episode/topic if known>
show📺 [[<title>]] <season>.<episode>
movie🎬 [[<title>]]
obsidian daily:append content="<entry>" 2>/dev/null | grep -Ev "^(20[0-9]{2}-|Your Obsidian)"

If the user provided season/episode or episode title in the arguments, use it; otherwise use a sensible placeholder or ask.

9. Re-index qmd

qmd update && qmd embed

Run this after the note is created and finalized so it's immediately searchable.

10. Report to user

Summarize what was done:

  • Note path created
  • Metadata filled (list key fields)
  • Cover art status (saved path or skip reason)
  • Daily note entry added
  • qmd indexed

Error handling

  • If note creation fails, check if the path already exists
  • If cover download returns a tiny image (< 5 KB), skip it and warn the user
  • If obsidian daily:append fails, tell the user the entry to add manually
  • If qmd embed takes too long, run it in the background with run_in_background: true

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.