-- plugins/tasks.lua-- Session task list as a single tool with an action verb.---- One tool with actions (add, list, done, remove) instead of four separate-- tools. Saves ~600 tokens per turn in schema overhead. local tasks = {}local next_id = 1 local function render() if #tasks == 0 then return "no tasks." end local lines = {} for _, t in ipairs(tasks) do lines[#lines + 1] = string.format( "[%s] %d. %s", t.done and "x" or " ", t.id, t.text) end return table.concat(lines, "\n")end local function find(id) for i, t in ipairs(tasks) do if t.id == id then return t, i end end return nil, nilend clm.tool_register("tasks", { description = "Manage the session task list. " .. "Actions: add, list, done, remove, update.", no_prompt = true, params_schema = { type = "object", properties = { action = { type = "string", description = "one of: add, list, done, remove, update", }, text = { type = "string", description = "task description (required for add and update)", }, id = { type = "integer", description = "task id (required for done, remove, and update)", }, }, required = { "action" }, }, invoke = function(args, ctx) local action = args.action if action == "add" then local text = args.text if not text or text == "" then ctx:fail("'add' requires a 'text' argument") return end local t = { id = next_id, text = text, done = false } next_id = next_id + 1 tasks[#tasks + 1] = t ctx:complete(string.format("added task %d: %s", t.id, t.text)) elseif action == "list" then ctx:complete(render()) elseif action == "done" then local id = args.id if type(id) ~= "number" then ctx:fail("'done' requires an 'id' argument") return end local t = find(id) if not t then ctx:fail(string.format("no task with id %d", id)) return end t.done = true ctx:complete(string.format("completed task %d: %s", t.id, t.text)) elseif action == "remove" then local id = args.id if type(id) ~= "number" then ctx:fail("'remove' requires an 'id' argument") return end local t, idx = find(id) if not t then ctx:fail(string.format("no task with id %d", id)) return end table.remove(tasks, idx) ctx:complete(string.format("removed task %d: %s", t.id, t.text)) elseif action == "update" then local id = args.id if type(id) ~= "number" then ctx:fail("'update' requires an 'id' argument") return end local text = args.text if not text or text == "" then ctx:fail("'update' requires a 'text' argument") return end local t = find(id) if not t then ctx:fail(string.format("no task with id %d", id)) return end t.text = text ctx:complete(string.format("updated task %d: %s", t.id, t.text)) else ctx:fail("unknown action '" .. tostring(action) .. "'; use add, list, done, remove, or update") end end,})
-- plugins/tasks.lua
-- Session task list as a single tool with an action verb.
--
-- One tool with actions (add, list, done, remove) instead of four separate
-- tools. Saves ~600 tokens per turn in schema overhead.
local tasks = {}
local next_id = 1
local function render()
if #tasks == 0 then
return "no tasks."
end
local lines = {}
for _, t in ipairs(tasks) do
lines[#lines + 1] = string.format(
"[%s] %d. %s", t.done and "x" or " ", t.id, t.text)
end
return table.concat(lines, "\n")
end
local function find(id)
for i, t in ipairs(tasks) do
if t.id == id then
return t, i
end
end
return nil, nil
end
clm.tool_register("tasks", {
description = "Manage the session task list. "
.. "Actions: add, list, done, remove, update.",
no_prompt = true,
params_schema = {
type = "object",
properties = {
action = {
type = "string",
description = "one of: add, list, done, remove, update",
},
text = {
type = "string",
description = "task description (required for add and update)",
},
id = {
type = "integer",
description = "task id (required for done, remove, and update)",
},
},
required = { "action" },
},
invoke = function(args, ctx)
local action = args.action
if action == "add" then
local text = args.text
if not text or text == "" then
ctx:fail("'add' requires a 'text' argument")
return
end
local t = { id = next_id, text = text, done = false }
next_id = next_id + 1
tasks[#tasks + 1] = t
ctx:complete(string.format("added task %d: %s", t.id, t.text))
elseif action == "list" then
ctx:complete(render())
elseif action == "done" then
local id = args.id
if type(id) ~= "number" then
ctx:fail("'done' requires an 'id' argument")
return
end
local t = find(id)
if not t then
ctx:fail(string.format("no task with id %d", id))
return
end
t.done = true
ctx:complete(string.format("completed task %d: %s", t.id, t.text))
elseif action == "remove" then
local id = args.id
if type(id) ~= "number" then
ctx:fail("'remove' requires an 'id' argument")
return
end
local t, idx = find(id)
if not t then
ctx:fail(string.format("no task with id %d", id))
return
end
table.remove(tasks, idx)
ctx:complete(string.format("removed task %d: %s", t.id, t.text))
elseif action == "update" then
local id = args.id
if type(id) ~= "number" then
ctx:fail("'update' requires an 'id' argument")
return
end
local text = args.text
if not text or text == "" then
ctx:fail("'update' requires a 'text' argument")
return
end
local t = find(id)
if not t then
ctx:fail(string.format("no task with id %d", id))
return
end
t.text = text
ctx:complete(string.format("updated task %d: %s", t.id, t.text))
else
ctx:fail("unknown action '" .. tostring(action)
.. "'; use add, list, done, remove, or update")
end
end,
})