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.
CLI
sb executes Lua on the remote SilverBullet instance. Use sb --help to explore available commands.
sb eval '<expr>'— single expression, prints its return valuesb script— multi-statement script from file (-f), inline argument, or stdin; usereturnfor output (printis swallowed)sb query '<sliq-expression>'— runs Space Lua Integrated Query directly (wraps inquery[[...]]for you)
# expression
sb eval 'space.readPage("index")'
# multi-statement via stdin
echo '
local text = space.readPage("index")
text = string.gsub(text, "old", "new")
space.writePage("index", text)
return "done"
' | sb script
Editing pages with plainReplace
string.gsub treats the search string as a Lua pattern, not plain text. Characters like -, ., %, and so on are magic and silently break replacements. Always use the plainReplace helper instead of raw string.gsub for find/replace on page content:
function plainReplace(str, old, new, n)
old = old:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")
new = new:gsub("%%", "%%%%")
if n == nil then
return str:gsub(old, new)
else
return str:gsub(old, new, n)
end
end
Usage: plainReplace(text, "arm64-v8a", "x86_64", 1)
Space Lua tripwires
- Space Lua APIs are camelCase:
space.readPage, notspace.read_page. - Use
return;print()output is swallowed. - Don't chain
:gsub()calls.gsubreturns(string, count), so reassign each step. - Don't use
queryas a variable or parameter name; it can parse as LIQ syntax. - The standard Lua
utf8library is unavailable. - In LIQ, use explicit binding:
from p = ....
Operations
# search (full-text, returns JSON with excerpts/scores/offsets)
sb eval 'silversearch.search("query", {silent=true})'
# read
sb eval 'space.readPage("PageName")'
sb eval 'space.getPageMeta("PageName")'
# query the object index (Space Lua Integrated Query)
sb query 'from p = index.tag "page" order by p.lastModified desc limit 10 select p.name'
# find pages that link to a specific page
sb query 'from l = index.tag "link" where l.toPage == "TargetPage" select l.fromPage'
# also available through eval, but the query wrapper is more convenient when sufficient
sb eval 'query[[from l = index.tag "link" where l.toPage == "TargetPage" select l.fromPage]]'
# write / create
sb eval 'space.writePage("PageName", "# Title\nContent")'
# delete
sb eval 'space.deletePage("PageName")'
# edit one-liner
sb eval 'space.writePage("Page", plainReplace(space.readPage("Page"), "typo", "fixed"))'
# multiple replacements
echo '
local function plainReplace(str, old, new, n)
old = old:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")
new = new:gsub("%%", "%%%%")
if n == nil then
return str:gsub(old, new)
else
return str:gsub(old, new, n)
end
end
local text = space.readPage("Title")
text = plainReplace(text, "mistake one", "correction one", 1) -- specify the replacement limit; you usually want 1
text = plainReplace(text, "mistake two", "correction two", 1)
space.writePage("Title", text)
return "done"
' | sb script
# writing multi-line Markdown with sb script
# Use [==[...]==] instead of [[...]] when Markdown contains wiki links
# or templates. If the content contains ]==], use a regular quoted string
# with \n escapes or edit existing content with plainReplace instead.
echo '
local text = [==[
Some content with [[wiki links]] and ${template directives}.
]==]
space.writePage("PageName", text)
return "done"
' | sb script
# If multi-line content ends with ]] immediately before the closing ]==],
# add a newline before the close and trim it off.
echo '
local text = [==[
Ends with [[Page]]
]==]:sub(1, -2)
space.writePage("PageName", text)
return "done"
' | sb script
Fetching SilverBullet docs
The official SilverBullet instance at silverbullet.md serves raw Markdown pages. When you need details on any Space Lua feature, API, or concept, fetch the docs directly:
# Fetch a specific docs page (URL-encode spaces and slashes)
sb eval 'net.readURI("https://silverbullet.md/.fs/Space%20Lua.md")'
# List all docs pages to find what you need
sb script <<'EOF'
local result = http.request("https://silverbullet.md/.fs", {
headers = { ["X-Sync-Mode"] = "true" }
})
local names = {}
for _, p in ipairs(result.body) do
table.insert(names, p.name)
end
return table.concat(names, "\n")
EOF
URL-encode page names (%20 for spaces, %2F for /). The page listing is always current, so no hardcoded index is needed.
If sb is unavailable when you run it, fetch the contents of https://silverbullet.md/.fs/CLI.md to read the installation instructions yourself and link the user to https://silverbullet.md/CLI so they can read it too. If they use mise.jdx.dev, the line to add to their global config is "github:silverbulletmd/silverbullet" = "latest" and the binary they want is sb.