tasks_update
Update multiple tasks by ID to a new status in bulk, ensuring tasks are not accidentally renamed or deleted. Provides a complete summary without needing additional calls to tasks_summary.
Instructions
Update tasks in bulk by ID to a different status. Returns complete summary no need to call tasks_summary afterwards. Prevents AI accidentally rename or deleting tasks during mass updates, not even possible
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | The IDs of existing tasks | |
| index | No | 0-based index to place the tasks. e.g.: - 0 for "Do this next" - Omit to place at the end ("Do this later") | |
| source_id | No | Source ID from task_setup() response - Defaults to most recent in the workspace if not provided - Try to always provide it! - If you don't have it, ask the user for a file path and call task_setup() | |
| status | Yes | You might need to infer it from the context: - "To Do" for tasks coming up next (e.g. "Do X next") - "In Progress" for what you'll do now (e.g. "First do X") - "Reminders" instructions for you (the AI) to be constantly reminded of - "Notes" to collect non-actionable notes - "Deleted" when they want these removed - Updating tasks to In Progress moves others to To Do, finishing a In Progress task moves the first Done to In Progress |
Implementation Reference
- src/tools.ts:117-151 (registration)Registration of the 'update' tool as 'tasks_update' via defineTool (with env.PREFIX_TOOLS prefix). Includes schema, description, fromArgs, and handler.update: defineTool('update', { schema: z.object({ source_id: schemas.sourceId, ids: schemas.ids, status: z.union([schemas.status, z.literal(env.STATUS_DELETED)]).describe(util.trimLines(` ${schemas.status.description} - "${env.STATUS_DELETED}" when they want these removed ${env.AUTO_WIP ? `- Updating tasks to ${env.STATUS_WIP} moves others to ${env.STATUS_TODO}, finishing a ${env.STATUS_WIP} task moves the first ${env.STATUS_DONE} to ${env.STATUS_WIP}` : ''} `)), index: schemas.index, }), fromArgs: ([taskIds, status]) => ({ ids: split(taskIds) || [], status }), description: 'Update tasks in bulk by ID to a different status. Returns complete summary no need to call tasks_summary afterwards. Prevents AI accidentally rename or deleting tasks during mass updates, not even possible', handler: (args, context = {}) => { const meta = metadata.load(args.source_id) const texts = args.ids.map((id) => { const task = meta.tasksByIdOrText[id] if (task) { return task.text } if (util.isId(id)) { throw new Error(`Task ID ${id} not found`) } // Assume the AI passed a text for a new task by mistake return id }) // Use add internally also for DELETED return tools.add.handler({ source_id: args.source_id, status: args.status, index: args.index, texts, }, { ...context, update: true }) }, }),
- src/tools.ts:130-150 (handler)Core handler for tasks_update: resolves task IDs to texts, then delegates to tasks_add handler for status changes or deletion.handler: (args, context = {}) => { const meta = metadata.load(args.source_id) const texts = args.ids.map((id) => { const task = meta.tasksByIdOrText[id] if (task) { return task.text } if (util.isId(id)) { throw new Error(`Task ID ${id} not found`) } // Assume the AI passed a text for a new task by mistake return id }) // Use add internally also for DELETED return tools.add.handler({ source_id: args.source_id, status: args.status, index: args.index, texts, }, { ...context, update: true }) },
- src/tools.ts:118-127 (schema)Zod input schema validation for tasks_update tool parameters.schema: z.object({ source_id: schemas.sourceId, ids: schemas.ids, status: z.union([schemas.status, z.literal(env.STATUS_DELETED)]).describe(util.trimLines(` ${schemas.status.description} - "${env.STATUS_DELETED}" when they want these removed ${env.AUTO_WIP ? `- Updating tasks to ${env.STATUS_WIP} moves others to ${env.STATUS_TODO}, finishing a ${env.STATUS_WIP} task moves the first ${env.STATUS_DONE} to ${env.STATUS_WIP}` : ''} `)), index: schemas.index, }),
- src/tools.ts:77-114 (helper)tasks_add handler, which implements the core logic for adding/updating/moving tasks between status groups and is reused by tasks_update.handler: (args, context) => { let meta = metadata.load(args.source_id) const { source, state } = meta const { texts, status } = args // Remove existing tasks with same text from all groups (duplicate handling) for (const groupName of meta.statuses) { if (state.groups[groupName]) { state.groups[groupName] = state.groups[groupName].filter(text => !texts.includes(text)) } } let group = state.groups[status] // Special handling for Deleted and other unknown statuses if (!group) { storage.save(source.path, state) return getSummary(source.id) } const wip = state.groups[env.STATUS_WIP] const todos = state.groups[env.STATUS_TODO] if (env.AUTO_WIP && args.status === env.STATUS_WIP) { // Move all WIP but the first to ToDo todos.unshift(...wip) wip.length = 0 } // Add new tasks at the specified index const index = util.clamp(args.index ?? group.length, 0, group.length) group.splice(index, 0, ...texts) const isUpdate = !!context?.update if (env.AUTO_WIP && !wip.length && todos[0] && (todos[0] !== texts[0] || isUpdate)) { // Move first ToDo to WIP (but not for updates) wip.push(todos.shift()!) } storage.save(source.path, state) // Re-load metadata after state changes meta = metadata.load(source.id) const affected = _.compact(texts.map(t => meta.tasksByIdOrText[t])) return getSummary(source.id, { [isUpdate ? 'updated' : 'added']: affected }) },
- src/tools.ts:197-214 (helper)defineTool utility that registers tools and applies 'tasks_' prefix to names if env.PREFIX_TOOLS is true, making 'update' into 'tasks_update'.function defineTool<S extends ZodSchema>(name: string, tool: { schema: S description: string isResource?: boolean isReadOnly?: boolean isEnabled?: boolean handler: (args: z.infer<S>, context?: any) => any fromArgs: (args: string[]) => z.infer<S> }) { const toolName = env.PREFIX_TOOLS ? `tasks_${name}` : name return { ...tool, name: toolName, isResource: tool.isResource ?? false, isReadOnly: tool.isReadOnly ?? false, isEnabled: tool.isEnabled ?? true, } }