@@ -8,6 +8,8 @@ SPDX-License-Identifier: CC0-1.0
SilverBullet MCP server, this repo uses jujutsu over git and imperative, kernel-style commits over Conventional Commits.
+We also maintain the SilverBullet agent skill (`SKILL.md`). It and the MCP server are separate surfaces: do not make the skill reference the MCP server, and do not make MCP prose reference the skill. When changing prose in either surface, consider whether the underlying rule applies to both, and if so, update each independently.
+
## Commands
```sh
@@ -0,0 +1,133 @@
+---
+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 value
+- `sb script` — multi-statement script from file (`-f`), inline argument, or stdin; use `return` for output (`print` is swallowed)
+- `sb query '<sliq-expression>'` — runs Space Lua Integrated Query directly (wraps in `query[[...]]` for you)
+
+```bash
+# 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:
+
+```lua
+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)`
+
+# Operations
+
+```bash
+# 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 long-string delimiters [==[...]==] instead of [[...]] when the
+# markdown itself contains [[ or ]], which would prematurely close a
+# plain Lua long string and cause syntax errors.
+echo '
+local text = [==[
+Some content with [[wiki links]] and ${template directives}.
+]==]
+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:
+
+```bash
+# 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`.