Silverbullet doesn’t assign a timestamp to the completed tasks by default.
But we can tell it to do this, with this workaround. use this space-lua script:
event.listen {
name = "task:stateChange",
run = function(e)
if e.data.newState == "x" and not e.data.text:find("%[completed:") then
local text = e.data.text
local completedAt = os.date("%Y-%m-%dT%H:%M:%S")
local newText = text:gsub("^%[%s*%]", "[x]") .. " [completed: " .. completedAt .. "]"
editor.dispatch({ changes = { from = e.data.from, to = e.data.to, insert = newText } })
end
end
}
What this does:
- Only trigger if the you just checked the box (‘x’) AND if there isn’t already a timestamp (prevents loops)
-
- Replace the empty checkbox with
-
- Append the timestamp to the end of the line
and with this in place you can sort a query based on the completed attribute.
This simple script can be even extended to also remove the completed attribute, if you uncheck the task.
that would look something like this:
event.listen {
name = "task:stateChange",
run = function(e)
local data = e.data
local text = data.text
-- Safety check: ensure text exists to avoid 'nil value' errors
if not text then return end
-- CASE 1: Task marked as complete ('x')
if data.newState == "x" then
-- Check if timestamp already exists to prevent infinite loops/double-clicks
if not string.find(text, "%[completed:") then
-- Some sandboxes restrict os.date; we use a simple format
local completedAt = os.date("%Y-%m-%dT%H:%M:%S")
-- Append the timestamp at the end
local newText = string.gsub(text, "%[%s*%]", "[x]") .. " [completed: " .. completedAt .. "]"
editor.dispatch({
changes = {
from = data.from,
to = data.to,
insert = newText
}
})
end
-- CASE 2: Task marked as incomplete (' ')
elseif data.newState == " " then
-- Check if there is actually a timestamp to remove
if string.find(text, "%[completed:") then
-- Remove the [completed: ...] tag and any leading space
local cleanText = string.gsub(text, "%[[xX]%]", "[ ]")
local newText = string.gsub(cleanText, "%s*%[completed: [^%]]+]", "")
editor.dispatch({
changes = {
from = data.from,
to = data.to,
insert = newText
}
})
end
end
end
}
