SKILL.md


name: using-silverbullet description: Manages notes in SilverBullet/SB via the CLI and Space Lua. Use when the user mentions notes, SilverBullet, pages, journal, tasks in their space, or asks to search/read/write/edit notes. license: GPL-3.0-or-later metadata: author: Amolith amolith@secluded.site

SilverBullet is a self-hosted Markdown note-taking app with a Lua scripting layer. Do not write top-level # Titles; SilverBullet puts the page title at the top of the page and Title\n\n# Title is redundant.

Prefer the high-level CLI

sb talks to a running SilverBullet instance through the Runtime API. Prefer get, describe, and query before dropping into Lua. Use sb <command> -h for full flags and edge cases.

Most useful commands:

  • sb space ls, sb space add, sb space rm <name> β€” manage saved space connections. Use -s <name> to select a space when more than one is configured.
  • sb get β€” list indexed tag names in the space.
  • sb describe [tag] β€” show query syntax and live schemas, e.g. sb describe page or sb describe task.
  • sb get <tag> [ref] β€” list indexed objects or fetch one object by ref. For pages, the ref is the page name.
  • sb query '<sliq-expression>' β€” run Space Lua Integrated Query; this wraps the argument in query[[...]].
  • sb eval '<expression>' β€” evaluate one Space Lua expression and print its return value.
  • sb script [code], sb script -f file.lua, or stdin β€” run multi-statement Space Lua. The positional argument is inline code, not a filename.
  • sb logs, sb logs -n 20, sb logs -f β€” inspect or follow headless client logs.

Use --json for parseable output, -o jsonl for line-oriented pipelines, and --text for human-readable output. --text is an output format, not β€œpage text”.

Common operations

# List recent page metadata
sb get page --sort-by lastModified:desc --limit 10 --select name,lastModified

# Find pages by name/prefix metadata
sb get page --where name:contains=meeting --select name,lastModified
sb get page --where name:startsWith=Journal/ --select name,lastModified

# Fetch one page metadata object. This does NOT return the Markdown body.
sb get page "Journal/2026-06-07" --json

# Read the Markdown body of a page
sb eval 'space.readPage("Journal/2026-06-07")'

# Full-text search; returns matches with excerpts, scores, and offsets
sb eval 'silversearch.search("search terms", {silent=true})'

# Tasks: prefer get filters for routine list/filter/sort work
sb get task --where done=false --sort-by priority:desc --limit 20
sb get task --where due:lte=2026-06-30 --where done=false

# Query when get cannot express the relationship
sb query 'from l = index.tag "link" where l.toPage == "TargetPage" select l.fromPage'
sb query 'from p = index.tag "page" order by p.lastModified desc limit 10 select p.name'

# Write/create only when asked to change the space
sb eval 'space.writePage("PageName", "Content without a duplicate top heading")'

# Delete only when explicitly asked
sb eval 'space.deletePage("PageName")'

sb get supports --where, -l/--selector, --sort-by, --limit, --offset, and --select; run sb get -h rather than memorising every filter operator.

Editing pages safely

string.gsub treats the search string as a Lua pattern, not plain text. Characters like -, ., and % are magic and can silently break replacements. Use a helper that returns only the replacement string:

local function plainReplace(str, old, new, n)
  old = old:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")
  new = new:gsub("%%", "%%%%")

  local replaced
  if n == nil then
    replaced = str:gsub(old, new)
  else
    replaced = str:gsub(old, new, n)
  end
  return replaced
end

Use sb script for multi-step edits:

sb script <<'EOF'
local function plainReplace(str, old, new, n)
  old = old:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")
  new = new:gsub("%%", "%%%%")

  local replaced
  if n == nil then
    replaced = str:gsub(old, new)
  else
    replaced = str:gsub(old, new, n)
  end
  return replaced
end

local text = space.readPage("PageName")
text = plainReplace(text, "old literal text", "new literal text", 1)
space.writePage("PageName", text)
return "done"
EOF

For multi-line Markdown literals, prefer [==[...]==] over [[...]] so wiki links and templates do not terminate the string accidentally. If the content itself contains ]==], use a normal quoted string with \n escapes or edit existing content with plainReplace.

Space Lua tripwires

  • Space Lua APIs are camelCase: space.readPage, not space.read_page.
  • Use return; print() output is swallowed.
  • Do not chain :gsub() calls. gsub returns (string, count), so reassign each step.
  • Do not use query as a variable or parameter name; it can parse as SLIQ syntax.
  • The standard Lua utf8 library is unavailable.
  • In SLIQ, use explicit binding: from p = ....
  • Use current command names: eval and script. Old lua/lua-script aliases are hidden compatibility shims.

SilverBullet docs

The official docs space serves raw Markdown pages. Fetch docs directly when you need detail on Space Lua, SLIQ, or the CLI:

curl -fsSL 'https://silverbullet.md/.fs/CLI.md'
curl -fsSL 'https://silverbullet.md/.fs/Space%20Lua.md'

URL-encode page names (%20 for spaces, %2F for /). If sb is unavailable, read https://silverbullet.md/.fs/CLI.md yourself and link the user to https://silverbullet.md/CLI.