/**
* Workflow prompts for task management
*/
import { z } from 'zod';
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { TodoistService } from '../services/todoist-service.js';
export function registerWorkflowPrompts(server: McpServer, todoist: TodoistService) {
// ─────────────────────────────────────────────────────────────
// daily_planning
// ─────────────────────────────────────────────────────────────
server.prompt(
'daily_planning',
'Morning planning workflow to review and prioritize your day',
async () => {
try {
// Gather context
const overdueTasks = await todoist.listTasks({ filter: 'overdue' });
const todayTasks = await todoist.listTasks({ filter: 'today' });
const projects = await todoist.listProjects();
const projectMap = new Map(projects.map(p => [p.id, p.name]));
// Format overdue tasks
const overdueList = overdueTasks.map(t => {
const project = projectMap.get(t.projectId) || 'Inbox';
const days = Math.abs(t.daysUntilDue || 0);
return `- "${t.content}" (${project}, ${days} day${days !== 1 ? 's' : ''} overdue)`;
}).join('\n');
// Format today's tasks
const todayList = todayTasks.map(t => {
const project = projectMap.get(t.projectId) || 'Inbox';
const priority = t.priority > 1 ? `P${5 - t.priority}` : '';
return `- "${t.content}" (${project}${priority ? ', ' + priority : ''})`;
}).join('\n');
const promptText = `# Daily Planning Session
Let's plan your day together. Here's your current situation:
## Overdue Tasks (${overdueTasks.length})
${overdueList || 'None! Great job staying current.'}
## Due Today (${todayTasks.length})
${todayList || 'Nothing scheduled for today.'}
---
**Let's work through this together:**
1. **Overdue Tasks Review**: For each overdue task, let's decide:
- Do it today?
- Reschedule to a specific date?
- Delete if no longer relevant?
2. **Today's Priority**: What are your top 3 priorities for today?
3. **New Tasks**: Is there anything else you need to capture?
4. **Time Blocking**: Do you have calendar time blocked for focused work?
What would you like to start with?`;
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: promptText,
},
},
],
};
} catch (error) {
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: `Error preparing daily planning: ${(error as Error).message}. Let's proceed manually - what tasks are on your mind for today?`,
},
},
],
};
}
}
);
// ─────────────────────────────────────────────────────────────
// weekly_review
// ─────────────────────────────────────────────────────────────
server.prompt(
'weekly_review',
'GTD-style weekly review workflow',
async () => {
try {
// Gather comprehensive context
const allTasks = await todoist.listTasks({ limit: 500 });
const projects = await todoist.listProjects();
const labels = await todoist.listLabels();
const projectMap = new Map(projects.map(p => [p.id, p.name]));
// Categorize tasks
const overdue = allTasks.filter(t => t.isOverdue);
const noDueDate = allTasks.filter(t => !t.due && !t.isCompleted);
const inbox = allTasks.filter(t => {
const project = projects.find(p => p.id === t.projectId);
return project?.isInboxProject && !t.isCompleted;
});
// Tasks by project
const tasksByProject: Map<string, number> = new Map();
for (const task of allTasks.filter(t => !t.isCompleted)) {
const projectName = projectMap.get(task.projectId) || 'Inbox';
tasksByProject.set(projectName, (tasksByProject.get(projectName) || 0) + 1);
}
const projectSummary = Array.from(tasksByProject.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([name, count]) => `- ${name}: ${count} tasks`)
.join('\n');
const promptText = `# Weekly Review
Let's do a comprehensive review of your task system. This GTD-style review helps ensure nothing falls through the cracks.
## Current State
**Overview:**
- Total active tasks: ${allTasks.filter(t => !t.isCompleted).length}
- Overdue: ${overdue.length}
- No due date: ${noDueDate.length}
- In Inbox (unprocessed): ${inbox.length}
- Projects: ${projects.length}
- Labels: ${labels.length}
**Tasks by Project:**
${projectSummary}
---
## Review Checklist
### 1. 📥 Process Inbox (${inbox.length} items)
${inbox.length > 0 ? 'You have unprocessed items in your Inbox. Each item should be:\n- Moved to the right project\n- Given a due date (if time-sensitive)\n- Labeled for context\n- Or deleted if not actionable' : '✅ Inbox is empty!'}
### 2. ⚠️ Clear Overdue (${overdue.length} items)
${overdue.length > 0 ? 'Review each overdue task:\n- Reschedule with a realistic date\n- Complete if quick\n- Delete if no longer relevant' : '✅ No overdue tasks!'}
### 3. 📋 Review Projects
Are any projects stuck? Missing next actions?
### 4. 🔮 Review Someday/Maybe
Any ideas ready to become active projects?
### 5. 🧠 Mind Dump
Anything on your mind that should be captured?
---
**Where would you like to start?** I can help you process your inbox, clear overdue tasks, or do a project-by-project review.`;
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: promptText,
},
},
],
};
} catch (error) {
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: `Error preparing weekly review: ${(error as Error).message}. Let's proceed with a manual review - what areas of your task system would you like to review?`,
},
},
],
};
}
}
);
// ─────────────────────────────────────────────────────────────
// quick_capture
// ─────────────────────────────────────────────────────────────
server.prompt(
'quick_capture',
'Rapidly capture multiple tasks or ideas',
{
tasks: z.string().optional().describe('Tasks to capture (one per line or comma-separated)'),
},
async ({ tasks }) => {
const promptText = tasks
? `# Quick Capture
I'll help you quickly capture these tasks:
\`\`\`
${tasks}
\`\`\`
For each item, I'll create a task in your Inbox. You can organize them later during your daily planning or weekly review.
Should I proceed with creating these tasks? Or would you like to add due dates or labels to any of them first?`
: `# Quick Capture Mode
Ready for rapid task capture! Share your tasks in any format:
**Examples:**
- One task per line
- Comma-separated: task 1, task 2, task 3
- With dates: "Call dentist tomorrow, Review report by Friday"
- With priority: "Urgent bug fix p1, Update docs p3"
All tasks will go to your Inbox for later processing. Just type or paste your tasks!`;
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: promptText,
},
},
],
};
}
);
// ─────────────────────────────────────────────────────────────
// task_breakdown
// ─────────────────────────────────────────────────────────────
server.prompt(
'task_breakdown',
'Break down a large task into actionable subtasks',
{
taskId: z.string().optional().describe('ID of the task to break down'),
taskDescription: z.string().optional().describe('Description of the task to break down (if no taskId)'),
},
async ({ taskId, taskDescription }) => {
let taskInfo = '';
if (taskId) {
try {
const task = await todoist.getTask(taskId);
const project = await todoist.getProject(task.projectId);
taskInfo = `## Task to Break Down
**Title:** ${task.content}
**Project:** ${project.name}
**Due:** ${task.due?.string || 'No due date'}
**Description:** ${task.description || 'None'}
`;
} catch {
taskInfo = `(Could not fetch task ${taskId})\n\n`;
}
} else if (taskDescription) {
taskInfo = `## Task to Break Down
**Description:** ${taskDescription}
`;
}
const promptText = `# Task Breakdown
${taskInfo}I'll help you break this down into smaller, actionable subtasks.
**Good subtasks are:**
- Concrete and specific
- Completable in one sitting (ideally < 2 hours)
- Start with a verb (Review, Write, Call, Research, etc.)
- Independent when possible
**Questions to consider:**
1. What's the very first physical action?
2. What information do you need to gather?
3. Who else needs to be involved?
4. What are the key milestones?
${taskId ? "I can create these as subtasks under the main task, or as separate tasks. What's the first thing that needs to happen?" : "Describe the task you'd like to break down, and I'll help you identify the subtasks."}`;
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: promptText,
},
},
],
};
}
);
}