assistant-skills/silverbullet-query/SKILL.md
2026-05-21 19:51:43 -05:00

4.9 KiB

name description
silverbullet-query 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:

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()}