create skill: silverbullet-query — SLIQ query/filter widgets for SilverBullet notes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Connor Rhodes 2026-05-21 19:51:43 -05:00
parent a26678119b
commit d7dfb6c0e2
2 changed files with 184 additions and 0 deletions

170
silverbullet-query/SKILL.md Normal file
View file

@ -0,0 +1,170 @@
---
name: silverbullet-query
description: Write Space Lua queries (SLIQ) for SilverBullet notes. Use this skill whenever the user wants to build a query, filter, or live view in their SilverBullet space — including filtering pages by folder or tag, listing tasks, showing recently modified notes, building dashboards, or embedding any kind of dynamic query widget in a note. Triggers on phrases like "make a query," "show all pages tagged," "build a filter," "list tasks in," "show recent notes," "create a silverbullet view," or "make a live widget."
---
# SilverBullet Query Skill
SilverBullet uses **SLIQ** (Space Lua Integrated Query) — an SQL-inspired query language embedded in Lua. Queries live inside `${...}` expressions that render as live widgets in any note.
## Quick reference
### Embedding a query in a note
Queries render inline using the `${...}` expression syntax:
```
${template.each(query[[
from p = index.tag "page"
where p.name:startsWith("proj/")
order by p.lastModified desc
]], templates.pageItem)}
```
Use `template.each(collection, template)` to render a list. Use `query[[...]]` alone when you just need the raw data or a single computed value.
### Data sources
| What you want | `from` clause |
|---|---|
| All pages | `from p = index.tag "page"` |
| All tasks | `from t = index.tag "task"` |
| All bullet items | `from i = index.tag "item"` |
| All paragraphs | `from p = index.tag "paragraph"` |
| All tags | `from t = index.tag "tag"` |
| A Lua list | `from n = {1, 2, 3}` |
Always use the explicit variable binding form (`p =`) — it's more reliable and future-proof.
### Filtering (`where`)
```
-- Pages in a folder
where p.name:startsWith("proj/")
-- Pages with a specific tag
where table.includes(p.tags, "account")
-- Pages matching name
where p.name:startsWith("Person")
-- Tasks on the current page only
where t.page == _CTX.currentPage.name
-- Combine conditions
where p.name:startsWith("proj/") and table.includes(p.tags, "active")
```
### Sorting (`order by`)
```
order by p.lastModified desc -- newest first
order by p.lastModified asc -- oldest first
order by p.name -- alphabetical
order by p.lastModified desc, p.name -- multi-key
```
Nulls default to last (asc) or first (desc). Override with `nulls first` / `nulls last`.
### Limits and offsets
```
limit 10
limit 10, 2 -- limit 10, skip first 2 (inline offset)
offset 5
```
### Selecting / transforming results
```
select p.name -- just the name
select { name = p.name, mod = p.lastModified } -- custom object
select p -- full object (default)
```
### Render templates
| Template | Renders as |
|---|---|
| `templates.pageItem` | `* [[page name]]` |
| `templates.fullPageItem` | `* [[page name\|page name]]` |
| `templates.taskItem` | `* [state] [[ref]] task text` |
| `templates.itemItem` | `* [[ref]] item text` |
| `templates.paragraphItem` | `* [[ref]] paragraph text` |
| `templates.tagItem` | `* [[tag:name\|#name]]` |
## Common patterns
### Pages in a folder, newest first
```
${template.each(query[[
from p = index.tag "page"
where p.name:startsWith("FOLDER/")
order by p.lastModified desc
]], templates.pageItem)}
```
### Pages with a given tag
```
${template.each(query[[
from p = index.tag "page"
where table.includes(p.tags, "TAGNAME")
order by p.lastModified desc
]], templates.pageItem)}
```
### Open tasks on the current page
```
${template.each(query[[
from t = index.tag "task"
where t.page == _CTX.currentPage.name and t.state == " "
]], templates.taskItem)}
```
### 5 most recently modified pages
```
${template.each(query[[
from p = index.tag "page"
order by p.lastModified desc
limit 5
]], templates.pageItem)}
```
### Inline count (no template)
```
${#query[[from p = index.tag "page" where p.name:startsWith("proj/")]]} projects
```
## How to write a query for the user
1. Ask what objects they want (pages, tasks, items, tags, etc.)
2. Ask what filter applies (folder prefix, tag, current page, a condition)
3. Ask how to sort (newest first is `lastModified desc`, alphabetical is `p.name`)
4. Ask if they want a limit
5. Pick the right `templates.*` for the object type
6. Wrap in `${template.each(...)}` and drop it into the note
If the user already described the intent (e.g., "show all proj pages newest first"), skip the questions and just write it.
## Placing the query
Queries go directly in the note as Markdown text — no fenced code block needed. The `${...}` syntax renders on Alt-click (or view mode). Drop the expression on its own line for clean rendering.
If the user wants the query to live in a `space-lua` block as a reusable function rather than an inline expression, wrap it:
```space-lua
function myView()
return template.each(query[[
from p = index.tag "page"
where p.name:startsWith("proj/")
order by p.lastModified desc
]], templates.pageItem)
end
```
Then call it anywhere: `${myView()}`