@vibetasks/mcp-server 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +972 -73
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -19,13 +19,17 @@ import {
|
|
|
19
19
|
containsError
|
|
20
20
|
} from "@vibetasks/shared";
|
|
21
21
|
import { z } from "zod";
|
|
22
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
23
|
+
function toJsonSchema(zodSchema) {
|
|
24
|
+
return zodToJsonSchema(zodSchema, { $refStrategy: "none" });
|
|
25
|
+
}
|
|
22
26
|
function setupTools(taskOps) {
|
|
23
|
-
|
|
27
|
+
const toolsWithZod = [
|
|
24
28
|
// create_task
|
|
25
29
|
{
|
|
26
30
|
name: "create_task",
|
|
27
|
-
description: "Create a new task in VibeTasks. Use this instead of TodoWrite - tasks persist across sessions and sync everywhere.
|
|
28
|
-
|
|
31
|
+
description: "Create a new task in VibeTasks. Use this instead of TodoWrite - tasks persist across sessions and sync everywhere.",
|
|
32
|
+
zodSchema: z.object({
|
|
29
33
|
title: z.string().describe("Task title (required)"),
|
|
30
34
|
description: z.string().optional().describe("Brief description of what this task involves"),
|
|
31
35
|
notes: z.string().optional().describe("Detailed notes in markdown"),
|
|
@@ -37,7 +41,7 @@ function setupTools(taskOps) {
|
|
|
37
41
|
due_date: z.string().optional().describe("Due date (ISO 8601 format)"),
|
|
38
42
|
priority: z.enum(["none", "low", "medium", "high"]).default("none").describe("Task priority"),
|
|
39
43
|
tags: z.array(z.string()).optional().describe("Tag names to attach"),
|
|
40
|
-
|
|
44
|
+
session_id: z.string().optional().describe("AI session ID for tracking task ownership. Pass this to link task to your session.")
|
|
41
45
|
}),
|
|
42
46
|
handler: async (args, taskOps2) => {
|
|
43
47
|
let tagIds = [];
|
|
@@ -60,15 +64,14 @@ function setupTools(taskOps) {
|
|
|
60
64
|
notes_format: "markdown",
|
|
61
65
|
due_date: args.due_date,
|
|
62
66
|
priority: args.priority,
|
|
63
|
-
subtasks_json: subtasksJson
|
|
67
|
+
subtasks_json: subtasksJson,
|
|
68
|
+
session_id: args.session_id,
|
|
69
|
+
created_by: "ai"
|
|
70
|
+
// Tasks created via MCP are always AI-created
|
|
64
71
|
});
|
|
65
72
|
if (tagIds.length > 0) {
|
|
66
73
|
await taskOps2.linkTaskTags(task.id, tagIds);
|
|
67
74
|
}
|
|
68
|
-
let finalTask = task;
|
|
69
|
-
if (args.start_vibing) {
|
|
70
|
-
finalTask = await taskOps2.updateTask(task.id, { status: "vibing" });
|
|
71
|
-
}
|
|
72
75
|
return {
|
|
73
76
|
content: [
|
|
74
77
|
{
|
|
@@ -77,16 +80,14 @@ function setupTools(taskOps) {
|
|
|
77
80
|
{
|
|
78
81
|
success: true,
|
|
79
82
|
task: {
|
|
80
|
-
id:
|
|
81
|
-
title:
|
|
82
|
-
description:
|
|
83
|
-
priority:
|
|
84
|
-
due_date:
|
|
85
|
-
status: finalTask.status,
|
|
83
|
+
id: task.id,
|
|
84
|
+
title: task.title,
|
|
85
|
+
description: task.description,
|
|
86
|
+
priority: task.priority,
|
|
87
|
+
due_date: task.due_date,
|
|
86
88
|
subtasks: subtasksJson,
|
|
87
|
-
created_at:
|
|
88
|
-
}
|
|
89
|
-
message: args.start_vibing ? "Task created and set to vibing status - you're now working on it!" : "Task created successfully"
|
|
89
|
+
created_at: task.created_at
|
|
90
|
+
}
|
|
90
91
|
},
|
|
91
92
|
null,
|
|
92
93
|
2
|
|
@@ -100,7 +101,7 @@ function setupTools(taskOps) {
|
|
|
100
101
|
{
|
|
101
102
|
name: "get_tasks",
|
|
102
103
|
description: "Get tasks by filter",
|
|
103
|
-
|
|
104
|
+
zodSchema: z.object({
|
|
104
105
|
filter: z.enum(["all", "today", "upcoming", "completed"]).default("all").describe("Task filter")
|
|
105
106
|
}),
|
|
106
107
|
handler: async (args, taskOps2) => {
|
|
@@ -134,7 +135,7 @@ function setupTools(taskOps) {
|
|
|
134
135
|
{
|
|
135
136
|
name: "update_task",
|
|
136
137
|
description: "Update an existing task",
|
|
137
|
-
|
|
138
|
+
zodSchema: z.object({
|
|
138
139
|
task_id: z.string().describe("Task ID"),
|
|
139
140
|
title: z.string().optional().describe("New title"),
|
|
140
141
|
notes: z.string().optional().describe("New notes (user-facing description)"),
|
|
@@ -179,7 +180,7 @@ function setupTools(taskOps) {
|
|
|
179
180
|
{
|
|
180
181
|
name: "complete_task",
|
|
181
182
|
description: "Mark a task as complete, optionally adding completion notes",
|
|
182
|
-
|
|
183
|
+
zodSchema: z.object({
|
|
183
184
|
task_id: z.string().describe("Task ID"),
|
|
184
185
|
context_notes: z.string().optional().describe("Completion notes - what was done, any final notes")
|
|
185
186
|
}),
|
|
@@ -215,7 +216,7 @@ function setupTools(taskOps) {
|
|
|
215
216
|
{
|
|
216
217
|
name: "delete_task",
|
|
217
218
|
description: "Delete a task",
|
|
218
|
-
|
|
219
|
+
zodSchema: z.object({
|
|
219
220
|
task_id: z.string().describe("Task ID")
|
|
220
221
|
}),
|
|
221
222
|
handler: async (args, taskOps2) => {
|
|
@@ -241,7 +242,7 @@ function setupTools(taskOps) {
|
|
|
241
242
|
{
|
|
242
243
|
name: "search_tasks",
|
|
243
244
|
description: "Search tasks by title",
|
|
244
|
-
|
|
245
|
+
zodSchema: z.object({
|
|
245
246
|
query: z.string().describe("Search query"),
|
|
246
247
|
limit: z.number().default(20).describe("Maximum results")
|
|
247
248
|
}),
|
|
@@ -274,15 +275,14 @@ function setupTools(taskOps) {
|
|
|
274
275
|
// create_task_with_subtasks
|
|
275
276
|
{
|
|
276
277
|
name: "create_task_with_subtasks",
|
|
277
|
-
description: "Create a parent task with multiple subtasks in one call. Use this when using TodoWrite - it automatically mirrors your todo list to TaskFlow for persistent tracking.
|
|
278
|
-
|
|
278
|
+
description: "Create a parent task with multiple subtasks in one call. Use this when using TodoWrite - it automatically mirrors your todo list to TaskFlow for persistent tracking.",
|
|
279
|
+
zodSchema: z.object({
|
|
279
280
|
title: z.string().describe("Parent task title (the overall goal)"),
|
|
280
281
|
subtasks: z.array(z.string()).describe("Array of subtask titles (one for each todo item)"),
|
|
281
282
|
notes: z.string().optional().describe("Notes for parent task"),
|
|
282
283
|
due_date: z.string().optional().describe("Due date for parent task (ISO 8601 format)"),
|
|
283
284
|
priority: z.enum(["none", "low", "medium", "high"]).default("none").describe("Priority for parent task"),
|
|
284
|
-
tags: z.array(z.string()).optional().describe("Tag names to attach to parent task")
|
|
285
|
-
start_vibing: z.boolean().default(false).describe(`If true, immediately set parent task to "vibing" status (you're starting work on it now)`)
|
|
285
|
+
tags: z.array(z.string()).optional().describe("Tag names to attach to parent task")
|
|
286
286
|
}),
|
|
287
287
|
handler: async (args, taskOps2) => {
|
|
288
288
|
let tagIds = [];
|
|
@@ -312,10 +312,6 @@ function setupTools(taskOps) {
|
|
|
312
312
|
});
|
|
313
313
|
subtasks.push(subtask);
|
|
314
314
|
}
|
|
315
|
-
let finalParentTask = parentTask;
|
|
316
|
-
if (args.start_vibing) {
|
|
317
|
-
finalParentTask = await taskOps2.updateTask(parentTask.id, { status: "vibing" });
|
|
318
|
-
}
|
|
319
315
|
return {
|
|
320
316
|
content: [
|
|
321
317
|
{
|
|
@@ -324,17 +320,15 @@ function setupTools(taskOps) {
|
|
|
324
320
|
{
|
|
325
321
|
success: true,
|
|
326
322
|
parent_task: {
|
|
327
|
-
id:
|
|
328
|
-
title:
|
|
329
|
-
priority:
|
|
330
|
-
status: finalParentTask.status,
|
|
323
|
+
id: parentTask.id,
|
|
324
|
+
title: parentTask.title,
|
|
325
|
+
priority: parentTask.priority,
|
|
331
326
|
subtask_count: subtasks.length
|
|
332
327
|
},
|
|
333
328
|
subtasks: subtasks.map((s) => ({
|
|
334
329
|
id: s.id,
|
|
335
330
|
title: s.title
|
|
336
|
-
}))
|
|
337
|
-
message: args.start_vibing ? "Parent task created and set to vibing status - you're now working on it!" : "Parent task and subtasks created successfully"
|
|
331
|
+
}))
|
|
338
332
|
},
|
|
339
333
|
null,
|
|
340
334
|
2
|
|
@@ -348,7 +342,7 @@ function setupTools(taskOps) {
|
|
|
348
342
|
{
|
|
349
343
|
name: "start_vibing",
|
|
350
344
|
description: 'Start vibing on a task (move to "vibing" status). Max 3 tasks vibing at once - research shows more causes 40% productivity loss.',
|
|
351
|
-
|
|
345
|
+
zodSchema: z.object({
|
|
352
346
|
task_id: z.string().describe("Task ID to start vibing on")
|
|
353
347
|
}),
|
|
354
348
|
handler: async (args, taskOps2) => {
|
|
@@ -402,7 +396,7 @@ function setupTools(taskOps) {
|
|
|
402
396
|
{
|
|
403
397
|
name: "stop_vibing",
|
|
404
398
|
description: 'Stop vibing on a task (move back to "todo" status). Use this when pausing work on a task to work on something else.',
|
|
405
|
-
|
|
399
|
+
zodSchema: z.object({
|
|
406
400
|
task_id: z.string().describe("Task ID to stop vibing on"),
|
|
407
401
|
context_notes: z.string().optional().describe("Optional: Save where you left off before stopping")
|
|
408
402
|
}),
|
|
@@ -438,12 +432,17 @@ function setupTools(taskOps) {
|
|
|
438
432
|
// get_vibing_tasks
|
|
439
433
|
{
|
|
440
434
|
name: "get_vibing_tasks",
|
|
441
|
-
description: 'Get all tasks currently in "vibing" status. Use this to check what the user is actively working on.',
|
|
442
|
-
|
|
435
|
+
description: 'Get all tasks currently in "vibing" status. Use this to check what the user is actively working on. Shows session_id to identify which AI session owns each task.',
|
|
436
|
+
zodSchema: z.object({
|
|
437
|
+
session_id: z.string().optional().describe("Filter by session ID to see only your tasks")
|
|
438
|
+
}),
|
|
443
439
|
handler: async (args, taskOps2) => {
|
|
444
440
|
const allTasks = await taskOps2.getTasks("all");
|
|
445
|
-
|
|
441
|
+
let vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
446
442
|
const WIP_LIMIT = 3;
|
|
443
|
+
if (args.session_id) {
|
|
444
|
+
vibingTasks = vibingTasks.filter((t) => t.session_id === args.session_id);
|
|
445
|
+
}
|
|
447
446
|
return {
|
|
448
447
|
content: [
|
|
449
448
|
{
|
|
@@ -456,11 +455,14 @@ function setupTools(taskOps) {
|
|
|
456
455
|
title: t.title,
|
|
457
456
|
context_notes: t.context_notes,
|
|
458
457
|
priority: t.priority,
|
|
459
|
-
due_date: t.due_date
|
|
458
|
+
due_date: t.due_date,
|
|
459
|
+
session_id: t.session_id,
|
|
460
|
+
created_by: t.created_by
|
|
460
461
|
})),
|
|
461
462
|
count: vibingTasks.length,
|
|
462
463
|
wip_limit: WIP_LIMIT,
|
|
463
|
-
at_limit: vibingTasks.length >= WIP_LIMIT
|
|
464
|
+
at_limit: vibingTasks.length >= WIP_LIMIT,
|
|
465
|
+
filtered_by_session: args.session_id || null
|
|
464
466
|
},
|
|
465
467
|
null,
|
|
466
468
|
2
|
|
@@ -474,7 +476,7 @@ function setupTools(taskOps) {
|
|
|
474
476
|
{
|
|
475
477
|
name: "set_context_notes",
|
|
476
478
|
description: 'Save "where I left off" notes for a task. Use this before pausing work or ending a session to capture context for later.',
|
|
477
|
-
|
|
479
|
+
zodSchema: z.object({
|
|
478
480
|
task_id: z.string().describe("Task ID"),
|
|
479
481
|
context_notes: z.string().describe("Where you left off - file, line, next step, blockers")
|
|
480
482
|
}),
|
|
@@ -508,7 +510,7 @@ function setupTools(taskOps) {
|
|
|
508
510
|
{
|
|
509
511
|
name: "list_tasks",
|
|
510
512
|
description: "List tasks filtered by status (todo, vibing, done) or all tasks",
|
|
511
|
-
|
|
513
|
+
zodSchema: z.object({
|
|
512
514
|
status: z.enum(["todo", "vibing", "done", "all"]).default("all").describe("Filter by status"),
|
|
513
515
|
limit: z.number().default(50).describe("Maximum results")
|
|
514
516
|
}),
|
|
@@ -550,7 +552,7 @@ function setupTools(taskOps) {
|
|
|
550
552
|
{
|
|
551
553
|
name: "log_ai_session",
|
|
552
554
|
description: "Manually log an AI session as a completed task",
|
|
553
|
-
|
|
555
|
+
zodSchema: z.object({
|
|
554
556
|
summary: z.string().describe("What was accomplished"),
|
|
555
557
|
files: z.array(z.string()).optional().describe("Files changed"),
|
|
556
558
|
duration_minutes: z.number().optional().describe("Session duration")
|
|
@@ -598,7 +600,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
598
600
|
{
|
|
599
601
|
name: "archive_task",
|
|
600
602
|
description: "Archive a completed task. Moves the task to archived status, hiding it from default views. Use this to clean up completed tasks without deleting them.",
|
|
601
|
-
|
|
603
|
+
zodSchema: z.object({
|
|
602
604
|
task_id: z.string().describe("Task ID to archive")
|
|
603
605
|
}),
|
|
604
606
|
handler: async (args, taskOps2) => {
|
|
@@ -630,7 +632,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
630
632
|
{
|
|
631
633
|
name: "get_archived_tasks",
|
|
632
634
|
description: "Get all archived tasks. Returns tasks that have been archived, sorted by archive date (most recent first).",
|
|
633
|
-
|
|
635
|
+
zodSchema: z.object({
|
|
634
636
|
limit: z.number().default(50).describe("Maximum number of archived tasks to return")
|
|
635
637
|
}),
|
|
636
638
|
handler: async (args, taskOps2) => {
|
|
@@ -664,7 +666,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
664
666
|
{
|
|
665
667
|
name: "unarchive_task",
|
|
666
668
|
description: "Restore an archived task. Moves the task from archived status back to done (or optionally another status).",
|
|
667
|
-
|
|
669
|
+
zodSchema: z.object({
|
|
668
670
|
task_id: z.string().describe("Task ID to unarchive"),
|
|
669
671
|
restore_status: z.enum(["todo", "vibing", "done"]).default("done").describe("Status to restore the task to (default: done)")
|
|
670
672
|
}),
|
|
@@ -696,7 +698,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
696
698
|
{
|
|
697
699
|
name: "capture_error",
|
|
698
700
|
description: "Parse error text from clipboard, terminal, or direct paste. Extracts structured information from common error formats (Node.js, Python, Expo, webpack, TypeScript, etc.) for AI consumption.",
|
|
699
|
-
|
|
701
|
+
zodSchema: z.object({
|
|
700
702
|
error_text: z.string().describe("The raw error text to parse"),
|
|
701
703
|
source: z.enum(["terminal", "browser", "paste", "clipboard", "sentry", "ci"]).default("paste").describe("Source of the error (helps with parsing)")
|
|
702
704
|
}),
|
|
@@ -743,7 +745,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
743
745
|
{
|
|
744
746
|
name: "summarize_errors",
|
|
745
747
|
description: "Summarize and deduplicate large error logs (up to thousands of lines). Groups errors by type/file, deduplicates similar errors using pattern matching, filters out node_modules frames, and returns a condensed summary (50-100 lines max).",
|
|
746
|
-
|
|
748
|
+
zodSchema: z.object({
|
|
747
749
|
log_text: z.string().describe("The raw log output containing errors"),
|
|
748
750
|
max_lines: z.number().default(100).describe("Maximum lines in the summary output (default: 100)")
|
|
749
751
|
}),
|
|
@@ -790,8 +792,8 @@ Generated by TaskFlow MCP Server`;
|
|
|
790
792
|
// update_subtask
|
|
791
793
|
{
|
|
792
794
|
name: "update_subtask",
|
|
793
|
-
description: "
|
|
794
|
-
|
|
795
|
+
description: "Update a subtask within a task. Mark it done, add progress notes, or update the title. Use this to track progress on individual steps.",
|
|
796
|
+
zodSchema: z.object({
|
|
795
797
|
task_id: z.string().describe("Parent task ID"),
|
|
796
798
|
subtask_id: z.string().describe("Subtask ID to update"),
|
|
797
799
|
done: z.boolean().optional().describe("Mark subtask as done/not done"),
|
|
@@ -816,12 +818,6 @@ Generated by TaskFlow MCP Server`;
|
|
|
816
818
|
if (args.title !== void 0) subtasks[subtaskIndex].title = args.title;
|
|
817
819
|
if (args.notes !== void 0) subtasks[subtaskIndex].notes = args.notes;
|
|
818
820
|
const updated = await taskOps2.updateTask(args.task_id, { subtasks_json: subtasks });
|
|
819
|
-
const doneCount = subtasks.filter((s) => s.done).length;
|
|
820
|
-
const totalCount = subtasks.length;
|
|
821
|
-
const percentage = Math.round(doneCount / totalCount * 100);
|
|
822
|
-
const barLength = 20;
|
|
823
|
-
const filled = Math.round(doneCount / totalCount * barLength);
|
|
824
|
-
const progressBar = "\u2588".repeat(filled) + "\u2591".repeat(barLength - filled);
|
|
825
821
|
return {
|
|
826
822
|
content: [
|
|
827
823
|
{
|
|
@@ -830,10 +826,9 @@ Generated by TaskFlow MCP Server`;
|
|
|
830
826
|
{
|
|
831
827
|
success: true,
|
|
832
828
|
task_id: args.task_id,
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
message: args.done ? `\u2713 Subtask marked done! ${doneCount}/${totalCount} complete.${doneCount === totalCount ? " \u{1F389} All subtasks complete!" : ""}` : `Subtask updated. ${doneCount}/${totalCount} complete.`
|
|
829
|
+
subtask: subtasks[subtaskIndex],
|
|
830
|
+
all_subtasks: subtasks,
|
|
831
|
+
progress: `${subtasks.filter((s) => s.done).length}/${subtasks.length} done`
|
|
837
832
|
},
|
|
838
833
|
null,
|
|
839
834
|
2
|
|
@@ -847,7 +842,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
847
842
|
{
|
|
848
843
|
name: "add_subtask",
|
|
849
844
|
description: "Add a new subtask to an existing task. Use this when you discover new steps while working.",
|
|
850
|
-
|
|
845
|
+
zodSchema: z.object({
|
|
851
846
|
task_id: z.string().describe("Parent task ID"),
|
|
852
847
|
title: z.string().describe("Subtask title"),
|
|
853
848
|
notes: z.string().optional().describe("Optional notes")
|
|
@@ -886,7 +881,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
886
881
|
{
|
|
887
882
|
name: "get_task_images",
|
|
888
883
|
description: "Get image attachments for a task. Returns signed URLs that can be viewed. Use this to see mockups, screenshots, or error images attached to a task.",
|
|
889
|
-
|
|
884
|
+
zodSchema: z.object({
|
|
890
885
|
task_id: z.string().describe("Task ID to get images for")
|
|
891
886
|
}),
|
|
892
887
|
handler: async (args, taskOps2) => {
|
|
@@ -930,7 +925,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
930
925
|
{
|
|
931
926
|
name: "get_current_task",
|
|
932
927
|
description: 'Get the task you should be working on right now. Returns the first "vibing" task with full details including subtasks, description, context notes, and images. Call this at the start of a session to know where you left off.',
|
|
933
|
-
|
|
928
|
+
zodSchema: z.object({}),
|
|
934
929
|
handler: async (_args, taskOps2) => {
|
|
935
930
|
const allTasks = await taskOps2.getTasks("all");
|
|
936
931
|
const vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
@@ -984,7 +979,7 @@ Generated by TaskFlow MCP Server`;
|
|
|
984
979
|
{
|
|
985
980
|
name: "report_error",
|
|
986
981
|
description: "Report an error you encountered while working. Creates a high-priority task to track and fix it. Use this when you hit a build error, runtime error, or any issue that blocks progress.",
|
|
987
|
-
|
|
982
|
+
zodSchema: z.object({
|
|
988
983
|
error_text: z.string().describe("The full error message including stack trace"),
|
|
989
984
|
context: z.string().optional().describe("What you were trying to do when this error occurred"),
|
|
990
985
|
file_path: z.string().optional().describe("File path where the error occurred (if known)"),
|
|
@@ -1068,7 +1063,7 @@ ${errorText}
|
|
|
1068
1063
|
{
|
|
1069
1064
|
name: "get_errors",
|
|
1070
1065
|
description: '[DEPRECATED - use get_inbox with source="errors" instead] Get current errors/bugs. This is a legacy tool - prefer get_inbox for unified inbox access.',
|
|
1071
|
-
|
|
1066
|
+
zodSchema: z.object({
|
|
1072
1067
|
priority: z.enum(["critical", "high", "all"]).default("all").describe("Filter by priority"),
|
|
1073
1068
|
source: z.enum(["sentry", "ci", "manual", "all"]).default("all").describe("Filter by source"),
|
|
1074
1069
|
unacknowledged_only: z.boolean().default(true).describe("Only show errors you haven't acknowledged yet"),
|
|
@@ -1115,7 +1110,7 @@ ${errorText}
|
|
|
1115
1110
|
{
|
|
1116
1111
|
name: "get_error_details",
|
|
1117
1112
|
description: "Get full details for a specific error including parsed error context, notes, stack trace, and file context. Use after get_inbox to dive into a specific issue.",
|
|
1118
|
-
|
|
1113
|
+
zodSchema: z.object({
|
|
1119
1114
|
error_id: z.string().describe("Error task ID")
|
|
1120
1115
|
}),
|
|
1121
1116
|
handler: async (args, taskOps2) => {
|
|
@@ -1175,7 +1170,7 @@ ${errorText}
|
|
|
1175
1170
|
{
|
|
1176
1171
|
name: "acknowledge_error",
|
|
1177
1172
|
description: "Mark an error as acknowledged so it doesn't keep appearing. Use this after you've investigated or fixed an error.",
|
|
1178
|
-
|
|
1173
|
+
zodSchema: z.object({
|
|
1179
1174
|
error_id: z.string().describe("Error task ID"),
|
|
1180
1175
|
action: z.enum(["investigating", "fixed", "wont_fix", "need_info", "delegated"]).describe("What action was taken"),
|
|
1181
1176
|
notes: z.string().optional().describe("Optional notes about what was done")
|
|
@@ -1225,7 +1220,7 @@ ${ackNote}`;
|
|
|
1225
1220
|
{
|
|
1226
1221
|
name: "get_inbox",
|
|
1227
1222
|
description: "Get inbox items from all external sources (Sentry errors, GitHub issues, email, Slack, CI failures). Use this to see what needs attention.",
|
|
1228
|
-
|
|
1223
|
+
zodSchema: z.object({
|
|
1229
1224
|
source: z.enum(["all", "sentry", "ci", "github", "email", "slack", "errors", "feedback"]).default("all").describe("Filter by source: all, sentry, ci, github, email, slack, errors (sentry+ci), feedback (github+email+slack)"),
|
|
1230
1225
|
priority: z.enum(["all", "high", "medium", "low"]).default("all").describe("Filter by priority"),
|
|
1231
1226
|
unacknowledged_only: z.boolean().default(true).describe("Only show items not yet acknowledged"),
|
|
@@ -1301,7 +1296,7 @@ ${ackNote}`;
|
|
|
1301
1296
|
{
|
|
1302
1297
|
name: "get_inbox_stats",
|
|
1303
1298
|
description: "Get a quick summary of inbox status - counts by source and priority. Check this first to understand what needs attention.",
|
|
1304
|
-
|
|
1299
|
+
zodSchema: z.object({}),
|
|
1305
1300
|
handler: async (_args, taskOps2) => {
|
|
1306
1301
|
const stats = await taskOps2.getInboxStats();
|
|
1307
1302
|
return {
|
|
@@ -1321,8 +1316,912 @@ ${ackNote}`;
|
|
|
1321
1316
|
]
|
|
1322
1317
|
};
|
|
1323
1318
|
}
|
|
1319
|
+
},
|
|
1320
|
+
// =============================================================================
|
|
1321
|
+
// SESSION MANAGEMENT TOOLS
|
|
1322
|
+
// =============================================================================
|
|
1323
|
+
// session_start - Start a new AI session
|
|
1324
|
+
{
|
|
1325
|
+
name: "session_start",
|
|
1326
|
+
description: "Start a new AI session for tracking work. Returns a memorable session ID. Call this at the beginning of a conversation to enable session tracking and crash recovery.",
|
|
1327
|
+
zodSchema: z.object({
|
|
1328
|
+
project: z.string().optional().describe("Project name (auto-detected if not provided)")
|
|
1329
|
+
}),
|
|
1330
|
+
handler: async (args, _taskOps) => {
|
|
1331
|
+
const adjectives = ["swift", "calm", "bold", "warm", "cool", "bright", "quick", "wise", "keen", "soft"];
|
|
1332
|
+
const nouns = ["fox", "owl", "bear", "wolf", "hawk", "moon", "star", "sun", "tree", "wave"];
|
|
1333
|
+
const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
1334
|
+
const noun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
1335
|
+
const num = Math.floor(Math.random() * 100);
|
|
1336
|
+
const sessionId = `${adj}-${noun}-${num}`;
|
|
1337
|
+
process.env.VIBETASKS_SESSION_ID = sessionId;
|
|
1338
|
+
process.env.VIBETASKS_SESSION_START = (/* @__PURE__ */ new Date()).toISOString();
|
|
1339
|
+
process.env.VIBETASKS_SESSION_PROJECT = args.project || "";
|
|
1340
|
+
return {
|
|
1341
|
+
content: [
|
|
1342
|
+
{
|
|
1343
|
+
type: "text",
|
|
1344
|
+
text: JSON.stringify(
|
|
1345
|
+
{
|
|
1346
|
+
success: true,
|
|
1347
|
+
session: {
|
|
1348
|
+
id: sessionId,
|
|
1349
|
+
started_at: process.env.VIBETASKS_SESSION_START,
|
|
1350
|
+
project: args.project || null
|
|
1351
|
+
},
|
|
1352
|
+
message: `Session "${sessionId}" started. Use this ID to continue work after a crash.`,
|
|
1353
|
+
tip: "If the AI crashes, tell the next one to continue from this session."
|
|
1354
|
+
},
|
|
1355
|
+
null,
|
|
1356
|
+
2
|
|
1357
|
+
)
|
|
1358
|
+
}
|
|
1359
|
+
]
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
},
|
|
1363
|
+
// session_current - Get current session info
|
|
1364
|
+
{
|
|
1365
|
+
name: "session_current",
|
|
1366
|
+
description: "Get the current AI session information. Returns session ID, start time, and project. Use this to check if a session is active.",
|
|
1367
|
+
zodSchema: z.object({}),
|
|
1368
|
+
handler: async (_args, _taskOps) => {
|
|
1369
|
+
const sessionId = process.env.VIBETASKS_SESSION_ID;
|
|
1370
|
+
const startTime = process.env.VIBETASKS_SESSION_START;
|
|
1371
|
+
const project = process.env.VIBETASKS_SESSION_PROJECT;
|
|
1372
|
+
if (!sessionId) {
|
|
1373
|
+
return {
|
|
1374
|
+
content: [
|
|
1375
|
+
{
|
|
1376
|
+
type: "text",
|
|
1377
|
+
text: JSON.stringify(
|
|
1378
|
+
{
|
|
1379
|
+
success: true,
|
|
1380
|
+
session: null,
|
|
1381
|
+
message: "No active session. Use session_start to begin a new session."
|
|
1382
|
+
},
|
|
1383
|
+
null,
|
|
1384
|
+
2
|
|
1385
|
+
)
|
|
1386
|
+
}
|
|
1387
|
+
]
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
const durationMs = startTime ? Date.now() - new Date(startTime).getTime() : 0;
|
|
1391
|
+
const durationMinutes = Math.floor(durationMs / 6e4);
|
|
1392
|
+
return {
|
|
1393
|
+
content: [
|
|
1394
|
+
{
|
|
1395
|
+
type: "text",
|
|
1396
|
+
text: JSON.stringify(
|
|
1397
|
+
{
|
|
1398
|
+
success: true,
|
|
1399
|
+
session: {
|
|
1400
|
+
id: sessionId,
|
|
1401
|
+
started_at: startTime,
|
|
1402
|
+
project: project || null,
|
|
1403
|
+
duration_minutes: durationMinutes,
|
|
1404
|
+
status: "active"
|
|
1405
|
+
}
|
|
1406
|
+
},
|
|
1407
|
+
null,
|
|
1408
|
+
2
|
|
1409
|
+
)
|
|
1410
|
+
}
|
|
1411
|
+
]
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
},
|
|
1415
|
+
// session_end - End the current session
|
|
1416
|
+
{
|
|
1417
|
+
name: "session_end",
|
|
1418
|
+
description: "End the current AI session with an optional summary. Use this at the end of a conversation to mark the session as complete.",
|
|
1419
|
+
zodSchema: z.object({
|
|
1420
|
+
summary: z.string().optional().describe("Summary of what was accomplished in this session"),
|
|
1421
|
+
handoff_notes: z.string().optional().describe("Notes for the next AI to continue the work")
|
|
1422
|
+
}),
|
|
1423
|
+
handler: async (args, _taskOps) => {
|
|
1424
|
+
const sessionId = process.env.VIBETASKS_SESSION_ID;
|
|
1425
|
+
const startTime = process.env.VIBETASKS_SESSION_START;
|
|
1426
|
+
const project = process.env.VIBETASKS_SESSION_PROJECT;
|
|
1427
|
+
if (!sessionId) {
|
|
1428
|
+
return {
|
|
1429
|
+
content: [
|
|
1430
|
+
{
|
|
1431
|
+
type: "text",
|
|
1432
|
+
text: JSON.stringify(
|
|
1433
|
+
{
|
|
1434
|
+
success: false,
|
|
1435
|
+
error: "No active session to end"
|
|
1436
|
+
},
|
|
1437
|
+
null,
|
|
1438
|
+
2
|
|
1439
|
+
)
|
|
1440
|
+
}
|
|
1441
|
+
]
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
const endTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
1445
|
+
const durationMs = startTime ? new Date(endTime).getTime() - new Date(startTime).getTime() : 0;
|
|
1446
|
+
const durationMinutes = Math.floor(durationMs / 6e4);
|
|
1447
|
+
delete process.env.VIBETASKS_SESSION_ID;
|
|
1448
|
+
delete process.env.VIBETASKS_SESSION_START;
|
|
1449
|
+
delete process.env.VIBETASKS_SESSION_PROJECT;
|
|
1450
|
+
return {
|
|
1451
|
+
content: [
|
|
1452
|
+
{
|
|
1453
|
+
type: "text",
|
|
1454
|
+
text: JSON.stringify(
|
|
1455
|
+
{
|
|
1456
|
+
success: true,
|
|
1457
|
+
session: {
|
|
1458
|
+
id: sessionId,
|
|
1459
|
+
started_at: startTime,
|
|
1460
|
+
ended_at: endTime,
|
|
1461
|
+
project: project || null,
|
|
1462
|
+
duration_minutes: durationMinutes,
|
|
1463
|
+
summary: args.summary || null,
|
|
1464
|
+
handoff_notes: args.handoff_notes || null,
|
|
1465
|
+
status: "ended"
|
|
1466
|
+
},
|
|
1467
|
+
message: `Session "${sessionId}" ended after ${durationMinutes} minutes.`
|
|
1468
|
+
},
|
|
1469
|
+
null,
|
|
1470
|
+
2
|
|
1471
|
+
)
|
|
1472
|
+
}
|
|
1473
|
+
]
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
},
|
|
1477
|
+
// session_log - Log an action or note to the current session
|
|
1478
|
+
{
|
|
1479
|
+
name: "session_log",
|
|
1480
|
+
description: "Log an action or note to the current session. Use this to record progress, decisions, or context that might be useful for crash recovery.",
|
|
1481
|
+
zodSchema: z.object({
|
|
1482
|
+
action_type: z.enum(["task_created", "task_completed", "task_updated", "file_modified", "note", "research_added", "error_captured"]).describe("Type of action being logged"),
|
|
1483
|
+
description: z.string().describe("Description of the action"),
|
|
1484
|
+
task_id: z.string().optional().describe("Related task ID (if applicable)"),
|
|
1485
|
+
file_path: z.string().optional().describe("Related file path (if applicable)")
|
|
1486
|
+
}),
|
|
1487
|
+
handler: async (args, _taskOps) => {
|
|
1488
|
+
const sessionId = process.env.VIBETASKS_SESSION_ID;
|
|
1489
|
+
if (!sessionId) {
|
|
1490
|
+
return {
|
|
1491
|
+
content: [
|
|
1492
|
+
{
|
|
1493
|
+
type: "text",
|
|
1494
|
+
text: JSON.stringify(
|
|
1495
|
+
{
|
|
1496
|
+
success: false,
|
|
1497
|
+
error: "No active session. Start one with session_start first."
|
|
1498
|
+
},
|
|
1499
|
+
null,
|
|
1500
|
+
2
|
|
1501
|
+
)
|
|
1502
|
+
}
|
|
1503
|
+
]
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1507
|
+
return {
|
|
1508
|
+
content: [
|
|
1509
|
+
{
|
|
1510
|
+
type: "text",
|
|
1511
|
+
text: JSON.stringify(
|
|
1512
|
+
{
|
|
1513
|
+
success: true,
|
|
1514
|
+
logged: {
|
|
1515
|
+
session_id: sessionId,
|
|
1516
|
+
action_type: args.action_type,
|
|
1517
|
+
description: args.description,
|
|
1518
|
+
task_id: args.task_id || null,
|
|
1519
|
+
file_path: args.file_path || null,
|
|
1520
|
+
timestamp
|
|
1521
|
+
},
|
|
1522
|
+
message: "Action logged to session."
|
|
1523
|
+
},
|
|
1524
|
+
null,
|
|
1525
|
+
2
|
|
1526
|
+
)
|
|
1527
|
+
}
|
|
1528
|
+
]
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
},
|
|
1532
|
+
// =============================================================================
|
|
1533
|
+
// RESEARCH CAPTURE TOOLS
|
|
1534
|
+
// =============================================================================
|
|
1535
|
+
// capture_research - Save research findings as a workspace doc
|
|
1536
|
+
{
|
|
1537
|
+
name: "capture_research",
|
|
1538
|
+
description: "Save research findings as a persistent workspace document. Use this when you explicitly research a topic and want to save the findings for future reference. The research doc will be available in the KnowledgeTab.",
|
|
1539
|
+
zodSchema: z.object({
|
|
1540
|
+
title: z.string().describe('Title for the research document (e.g., "React Server Components Best Practices")'),
|
|
1541
|
+
content: z.string().describe("Research findings in markdown format"),
|
|
1542
|
+
source_urls: z.array(z.string()).optional().describe("URLs that were researched"),
|
|
1543
|
+
search_queries: z.array(z.string()).optional().describe("Search queries used to find this information"),
|
|
1544
|
+
project: z.string().optional().describe("Project this research applies to (defaults to Sparktory)"),
|
|
1545
|
+
applies_to_children: z.boolean().default(true).describe("If true, this research will be available to child workspaces")
|
|
1546
|
+
}),
|
|
1547
|
+
handler: async (args, taskOps2) => {
|
|
1548
|
+
try {
|
|
1549
|
+
const doc = await taskOps2.createWorkspaceDoc({
|
|
1550
|
+
doc_type: "research",
|
|
1551
|
+
title: args.title,
|
|
1552
|
+
content: args.content,
|
|
1553
|
+
project_tag: args.project || "sparktory",
|
|
1554
|
+
created_by: "ai",
|
|
1555
|
+
source_urls: args.source_urls || [],
|
|
1556
|
+
search_queries: args.search_queries || [],
|
|
1557
|
+
applies_to_children: args.applies_to_children ?? true
|
|
1558
|
+
});
|
|
1559
|
+
const sessionId = process.env.VIBETASKS_SESSION_ID;
|
|
1560
|
+
return {
|
|
1561
|
+
content: [
|
|
1562
|
+
{
|
|
1563
|
+
type: "text",
|
|
1564
|
+
text: JSON.stringify(
|
|
1565
|
+
{
|
|
1566
|
+
success: true,
|
|
1567
|
+
research: {
|
|
1568
|
+
id: doc.id,
|
|
1569
|
+
title: args.title,
|
|
1570
|
+
project: doc.project_tag,
|
|
1571
|
+
source_count: args.source_urls?.length || 0,
|
|
1572
|
+
applies_to_children: args.applies_to_children ?? true
|
|
1573
|
+
},
|
|
1574
|
+
session_id: sessionId || null,
|
|
1575
|
+
message: `Research "${args.title}" saved to ${doc.project_tag} workspace.`,
|
|
1576
|
+
hint: "View in the Knowledge tab under Research docs."
|
|
1577
|
+
},
|
|
1578
|
+
null,
|
|
1579
|
+
2
|
|
1580
|
+
)
|
|
1581
|
+
}
|
|
1582
|
+
]
|
|
1583
|
+
};
|
|
1584
|
+
} catch (error) {
|
|
1585
|
+
return {
|
|
1586
|
+
content: [
|
|
1587
|
+
{
|
|
1588
|
+
type: "text",
|
|
1589
|
+
text: JSON.stringify(
|
|
1590
|
+
{
|
|
1591
|
+
success: false,
|
|
1592
|
+
error: error.message || "Failed to capture research"
|
|
1593
|
+
},
|
|
1594
|
+
null,
|
|
1595
|
+
2
|
|
1596
|
+
)
|
|
1597
|
+
}
|
|
1598
|
+
]
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
},
|
|
1603
|
+
// create_plan - Create a plan/roadmap with phases
|
|
1604
|
+
{
|
|
1605
|
+
name: "create_plan",
|
|
1606
|
+
description: "Create a plan or roadmap with phases/milestones as subtasks. Plans track progress based on subtask completion percentage. Use this for architecture decisions, feature roadmaps, or multi-phase projects.",
|
|
1607
|
+
zodSchema: z.object({
|
|
1608
|
+
title: z.string().describe('Plan title (e.g., "Q1 Feature Roadmap", "Auth System Architecture")'),
|
|
1609
|
+
description: z.string().optional().describe("Overview of what this plan covers"),
|
|
1610
|
+
phases: z.array(z.object({
|
|
1611
|
+
title: z.string().describe("Phase/milestone title"),
|
|
1612
|
+
notes: z.string().optional().describe("Details about this phase")
|
|
1613
|
+
})).describe("Phases or milestones of the plan"),
|
|
1614
|
+
project: z.string().optional().describe("Project this plan belongs to"),
|
|
1615
|
+
priority: z.enum(["none", "low", "medium", "high"]).default("medium").describe("Plan priority"),
|
|
1616
|
+
due_date: z.string().optional().describe("Target completion date (ISO 8601)")
|
|
1617
|
+
}),
|
|
1618
|
+
handler: async (args, taskOps2) => {
|
|
1619
|
+
try {
|
|
1620
|
+
const subtasksJson = args.phases.map((phase) => ({
|
|
1621
|
+
id: crypto.randomUUID(),
|
|
1622
|
+
title: phase.title,
|
|
1623
|
+
done: false,
|
|
1624
|
+
notes: phase.notes
|
|
1625
|
+
}));
|
|
1626
|
+
const plan = await taskOps2.createTask({
|
|
1627
|
+
title: args.title,
|
|
1628
|
+
description: args.description,
|
|
1629
|
+
notes: args.description ? `# ${args.title}
|
|
1630
|
+
|
|
1631
|
+
${args.description}` : `# ${args.title}`,
|
|
1632
|
+
notes_format: "markdown",
|
|
1633
|
+
priority: args.priority,
|
|
1634
|
+
due_date: args.due_date,
|
|
1635
|
+
project_tag: args.project,
|
|
1636
|
+
subtasks_json: subtasksJson,
|
|
1637
|
+
is_plan: true,
|
|
1638
|
+
created_by: "ai"
|
|
1639
|
+
});
|
|
1640
|
+
const progress = `0/${args.phases.length} phases`;
|
|
1641
|
+
return {
|
|
1642
|
+
content: [
|
|
1643
|
+
{
|
|
1644
|
+
type: "text",
|
|
1645
|
+
text: JSON.stringify(
|
|
1646
|
+
{
|
|
1647
|
+
success: true,
|
|
1648
|
+
plan: {
|
|
1649
|
+
id: plan.id,
|
|
1650
|
+
title: plan.title,
|
|
1651
|
+
phases: subtasksJson.map((s) => ({ id: s.id, title: s.title })),
|
|
1652
|
+
phase_count: args.phases.length,
|
|
1653
|
+
progress,
|
|
1654
|
+
project: args.project || null,
|
|
1655
|
+
due_date: args.due_date || null
|
|
1656
|
+
},
|
|
1657
|
+
message: `Plan "${args.title}" created with ${args.phases.length} phases.`,
|
|
1658
|
+
hint: "Use update_subtask to mark phases complete as you progress."
|
|
1659
|
+
},
|
|
1660
|
+
null,
|
|
1661
|
+
2
|
|
1662
|
+
)
|
|
1663
|
+
}
|
|
1664
|
+
]
|
|
1665
|
+
};
|
|
1666
|
+
} catch (error) {
|
|
1667
|
+
return {
|
|
1668
|
+
content: [
|
|
1669
|
+
{
|
|
1670
|
+
type: "text",
|
|
1671
|
+
text: JSON.stringify(
|
|
1672
|
+
{
|
|
1673
|
+
success: false,
|
|
1674
|
+
error: error.message || "Failed to create plan"
|
|
1675
|
+
},
|
|
1676
|
+
null,
|
|
1677
|
+
2
|
|
1678
|
+
)
|
|
1679
|
+
}
|
|
1680
|
+
]
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1685
|
+
// get_plans - List all plans
|
|
1686
|
+
{
|
|
1687
|
+
name: "get_plans",
|
|
1688
|
+
description: "Get all plans/roadmaps. Shows progress as percentage of completed phases.",
|
|
1689
|
+
zodSchema: z.object({
|
|
1690
|
+
project: z.string().optional().describe("Filter by project"),
|
|
1691
|
+
include_completed: z.boolean().default(false).describe("Include completed plans"),
|
|
1692
|
+
limit: z.number().default(20).describe("Maximum plans to return")
|
|
1693
|
+
}),
|
|
1694
|
+
handler: async (args, taskOps2) => {
|
|
1695
|
+
try {
|
|
1696
|
+
const allTasks = await taskOps2.getTasks("all");
|
|
1697
|
+
let plans = allTasks.filter((t) => t.is_plan === true);
|
|
1698
|
+
if (args.project) {
|
|
1699
|
+
plans = plans.filter(
|
|
1700
|
+
(t) => t.project_tag?.toLowerCase() === args.project?.toLowerCase()
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
if (!args.include_completed) {
|
|
1704
|
+
plans = plans.filter((t) => !t.completed);
|
|
1705
|
+
}
|
|
1706
|
+
plans = plans.slice(0, args.limit);
|
|
1707
|
+
return {
|
|
1708
|
+
content: [
|
|
1709
|
+
{
|
|
1710
|
+
type: "text",
|
|
1711
|
+
text: JSON.stringify(
|
|
1712
|
+
{
|
|
1713
|
+
success: true,
|
|
1714
|
+
plans: plans.map((t) => {
|
|
1715
|
+
const subtasks = t.subtasks_json || [];
|
|
1716
|
+
const completed = subtasks.filter((s) => s.done).length;
|
|
1717
|
+
const total = subtasks.length;
|
|
1718
|
+
const percentage = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
1719
|
+
return {
|
|
1720
|
+
id: t.id,
|
|
1721
|
+
title: t.title,
|
|
1722
|
+
project: t.project_tag,
|
|
1723
|
+
progress: `${completed}/${total} (${percentage}%)`,
|
|
1724
|
+
percentage,
|
|
1725
|
+
due_date: t.due_date,
|
|
1726
|
+
phases: subtasks.map((s) => ({
|
|
1727
|
+
id: s.id,
|
|
1728
|
+
title: s.title,
|
|
1729
|
+
done: s.done
|
|
1730
|
+
}))
|
|
1731
|
+
};
|
|
1732
|
+
}),
|
|
1733
|
+
count: plans.length,
|
|
1734
|
+
filter: { project: args.project, include_completed: args.include_completed }
|
|
1735
|
+
},
|
|
1736
|
+
null,
|
|
1737
|
+
2
|
|
1738
|
+
)
|
|
1739
|
+
}
|
|
1740
|
+
]
|
|
1741
|
+
};
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
return {
|
|
1744
|
+
content: [
|
|
1745
|
+
{
|
|
1746
|
+
type: "text",
|
|
1747
|
+
text: JSON.stringify(
|
|
1748
|
+
{
|
|
1749
|
+
success: false,
|
|
1750
|
+
error: error.message || "Failed to get plans"
|
|
1751
|
+
},
|
|
1752
|
+
null,
|
|
1753
|
+
2
|
|
1754
|
+
)
|
|
1755
|
+
}
|
|
1756
|
+
]
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
},
|
|
1761
|
+
// quick_checkpoint - Ultra-fast context save (Windsurf-inspired)
|
|
1762
|
+
{
|
|
1763
|
+
name: "quick_checkpoint",
|
|
1764
|
+
description: "Ultra-fast context checkpoint for the current vibing task. Auto-detects what changed and saves it. Use this frequently during work to create a crash-recovery trail. Zero-friction alternative to update_task.",
|
|
1765
|
+
zodSchema: z.object({
|
|
1766
|
+
completed: z.string().optional().describe("What you just completed (optional)"),
|
|
1767
|
+
next: z.string().optional().describe("What's next (optional)"),
|
|
1768
|
+
files_changed: z.array(z.string()).optional().describe("Files you modified (optional, will be auto-detected if git available)"),
|
|
1769
|
+
blocker: z.string().optional().describe("Any blocker encountered (optional)")
|
|
1770
|
+
}),
|
|
1771
|
+
handler: async (args, taskOps2) => {
|
|
1772
|
+
try {
|
|
1773
|
+
const allTasks = await taskOps2.getTasks("all");
|
|
1774
|
+
const vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
1775
|
+
if (vibingTasks.length === 0) {
|
|
1776
|
+
return {
|
|
1777
|
+
content: [
|
|
1778
|
+
{
|
|
1779
|
+
type: "text",
|
|
1780
|
+
text: JSON.stringify(
|
|
1781
|
+
{
|
|
1782
|
+
success: false,
|
|
1783
|
+
error: "No task currently vibing. Use start_vibing first."
|
|
1784
|
+
},
|
|
1785
|
+
null,
|
|
1786
|
+
2
|
|
1787
|
+
)
|
|
1788
|
+
}
|
|
1789
|
+
]
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
const task = vibingTasks[0];
|
|
1793
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1794
|
+
const parts = [`Checkpoint at ${timestamp}`];
|
|
1795
|
+
if (args.completed) {
|
|
1796
|
+
parts.push(`
|
|
1797
|
+
## Completed
|
|
1798
|
+
${args.completed}`);
|
|
1799
|
+
}
|
|
1800
|
+
if (args.files_changed && args.files_changed.length > 0) {
|
|
1801
|
+
parts.push(`
|
|
1802
|
+
## Files Changed
|
|
1803
|
+
${args.files_changed.map((f) => `- ${f}`).join("\n")}`);
|
|
1804
|
+
}
|
|
1805
|
+
if (args.next) {
|
|
1806
|
+
parts.push(`
|
|
1807
|
+
## Next
|
|
1808
|
+
${args.next}`);
|
|
1809
|
+
}
|
|
1810
|
+
if (args.blocker) {
|
|
1811
|
+
parts.push(`
|
|
1812
|
+
## Blocker
|
|
1813
|
+
${args.blocker}`);
|
|
1814
|
+
}
|
|
1815
|
+
const contextNotes = parts.join("\n");
|
|
1816
|
+
await taskOps2.updateTask(task.id, { context_notes: contextNotes });
|
|
1817
|
+
return {
|
|
1818
|
+
content: [
|
|
1819
|
+
{
|
|
1820
|
+
type: "text",
|
|
1821
|
+
text: JSON.stringify(
|
|
1822
|
+
{
|
|
1823
|
+
success: true,
|
|
1824
|
+
checkpointed: {
|
|
1825
|
+
task_id: task.id,
|
|
1826
|
+
task_title: task.title,
|
|
1827
|
+
timestamp,
|
|
1828
|
+
has_completed: !!args.completed,
|
|
1829
|
+
has_next: !!args.next,
|
|
1830
|
+
has_blocker: !!args.blocker,
|
|
1831
|
+
files_count: args.files_changed?.length || 0
|
|
1832
|
+
},
|
|
1833
|
+
message: "Checkpoint saved. Context preserved for crash recovery."
|
|
1834
|
+
},
|
|
1835
|
+
null,
|
|
1836
|
+
2
|
|
1837
|
+
)
|
|
1838
|
+
}
|
|
1839
|
+
]
|
|
1840
|
+
};
|
|
1841
|
+
} catch (error) {
|
|
1842
|
+
return {
|
|
1843
|
+
content: [
|
|
1844
|
+
{
|
|
1845
|
+
type: "text",
|
|
1846
|
+
text: JSON.stringify(
|
|
1847
|
+
{
|
|
1848
|
+
success: false,
|
|
1849
|
+
error: error.message || "Failed to checkpoint"
|
|
1850
|
+
},
|
|
1851
|
+
null,
|
|
1852
|
+
2
|
|
1853
|
+
)
|
|
1854
|
+
}
|
|
1855
|
+
]
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
},
|
|
1860
|
+
// get_research - Get saved research docs from workspace_docs table
|
|
1861
|
+
{
|
|
1862
|
+
name: "get_research",
|
|
1863
|
+
description: "Get saved research documents from the knowledge base. Use this to recall research findings from previous sessions.",
|
|
1864
|
+
zodSchema: z.object({
|
|
1865
|
+
project: z.string().optional().describe("Filter by project (defaults to all)"),
|
|
1866
|
+
search: z.string().optional().describe("Search term to filter research docs"),
|
|
1867
|
+
limit: z.number().default(10).describe("Maximum docs to return")
|
|
1868
|
+
}),
|
|
1869
|
+
handler: async (args, taskOps2) => {
|
|
1870
|
+
try {
|
|
1871
|
+
const docs = await taskOps2.getWorkspaceDocs({
|
|
1872
|
+
doc_type: "research",
|
|
1873
|
+
project_tag: args.project,
|
|
1874
|
+
search: args.search,
|
|
1875
|
+
limit: args.limit
|
|
1876
|
+
});
|
|
1877
|
+
return {
|
|
1878
|
+
content: [
|
|
1879
|
+
{
|
|
1880
|
+
type: "text",
|
|
1881
|
+
text: JSON.stringify(
|
|
1882
|
+
{
|
|
1883
|
+
success: true,
|
|
1884
|
+
research_docs: (docs || []).map((d) => ({
|
|
1885
|
+
id: d.id,
|
|
1886
|
+
title: d.title,
|
|
1887
|
+
project: d.project_tag,
|
|
1888
|
+
created_at: d.created_at,
|
|
1889
|
+
created_by: d.created_by,
|
|
1890
|
+
source_urls: d.source_urls || [],
|
|
1891
|
+
preview: d.content?.substring(0, 300) + (d.content && d.content.length > 300 ? "..." : "")
|
|
1892
|
+
})),
|
|
1893
|
+
count: docs?.length || 0,
|
|
1894
|
+
filter: { project: args.project, search: args.search }
|
|
1895
|
+
},
|
|
1896
|
+
null,
|
|
1897
|
+
2
|
|
1898
|
+
)
|
|
1899
|
+
}
|
|
1900
|
+
]
|
|
1901
|
+
};
|
|
1902
|
+
} catch (error) {
|
|
1903
|
+
return {
|
|
1904
|
+
content: [
|
|
1905
|
+
{
|
|
1906
|
+
type: "text",
|
|
1907
|
+
text: JSON.stringify(
|
|
1908
|
+
{
|
|
1909
|
+
success: false,
|
|
1910
|
+
error: error.message || "Failed to get research docs"
|
|
1911
|
+
},
|
|
1912
|
+
null,
|
|
1913
|
+
2
|
|
1914
|
+
)
|
|
1915
|
+
}
|
|
1916
|
+
]
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
// =========================================================================
|
|
1922
|
+
// GIT-FOR-TASKS: Change Tracking Tools
|
|
1923
|
+
// =========================================================================
|
|
1924
|
+
// get_task_history - Like git log for a task
|
|
1925
|
+
{
|
|
1926
|
+
name: "get_task_history",
|
|
1927
|
+
description: "Get change history for a task (like git log). Shows who changed what and when. Use this to understand how a task evolved or to find a change to revert.",
|
|
1928
|
+
zodSchema: z.object({
|
|
1929
|
+
task_id: z.string().describe("Task ID to get history for"),
|
|
1930
|
+
limit: z.number().default(20).describe("Maximum changes to return"),
|
|
1931
|
+
change_types: z.array(z.enum([
|
|
1932
|
+
"created",
|
|
1933
|
+
"status_changed",
|
|
1934
|
+
"title_changed",
|
|
1935
|
+
"context_updated",
|
|
1936
|
+
"priority_changed",
|
|
1937
|
+
"subtask_done",
|
|
1938
|
+
"subtask_undone",
|
|
1939
|
+
"subtask_added",
|
|
1940
|
+
"archived",
|
|
1941
|
+
"restored",
|
|
1942
|
+
"revert"
|
|
1943
|
+
])).optional().describe("Filter by specific change types")
|
|
1944
|
+
}),
|
|
1945
|
+
handler: async (args, taskOps2) => {
|
|
1946
|
+
try {
|
|
1947
|
+
const changes = await taskOps2.getTaskChanges(args.task_id, {
|
|
1948
|
+
limit: args.limit,
|
|
1949
|
+
changeTypes: args.change_types
|
|
1950
|
+
});
|
|
1951
|
+
const task = await taskOps2.getTask(args.task_id);
|
|
1952
|
+
return {
|
|
1953
|
+
content: [
|
|
1954
|
+
{
|
|
1955
|
+
type: "text",
|
|
1956
|
+
text: JSON.stringify(
|
|
1957
|
+
{
|
|
1958
|
+
success: true,
|
|
1959
|
+
task: {
|
|
1960
|
+
id: task.id,
|
|
1961
|
+
title: task.title,
|
|
1962
|
+
status: task.status
|
|
1963
|
+
},
|
|
1964
|
+
changes: changes.map((c) => ({
|
|
1965
|
+
id: c.id,
|
|
1966
|
+
change_type: c.change_type,
|
|
1967
|
+
message: c.message,
|
|
1968
|
+
actor_type: c.actor_type,
|
|
1969
|
+
field_changed: c.field_changed,
|
|
1970
|
+
old_value: c.old_value,
|
|
1971
|
+
new_value: c.new_value,
|
|
1972
|
+
session_id: c.session_id,
|
|
1973
|
+
created_at: c.created_at
|
|
1974
|
+
})),
|
|
1975
|
+
count: changes.length
|
|
1976
|
+
},
|
|
1977
|
+
null,
|
|
1978
|
+
2
|
|
1979
|
+
)
|
|
1980
|
+
}
|
|
1981
|
+
]
|
|
1982
|
+
};
|
|
1983
|
+
} catch (error) {
|
|
1984
|
+
return {
|
|
1985
|
+
content: [
|
|
1986
|
+
{
|
|
1987
|
+
type: "text",
|
|
1988
|
+
text: JSON.stringify({ success: false, error: error.message }, null, 2)
|
|
1989
|
+
}
|
|
1990
|
+
]
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
},
|
|
1995
|
+
// revert_change - Like git revert
|
|
1996
|
+
{
|
|
1997
|
+
name: "revert_change",
|
|
1998
|
+
description: 'Revert a specific change (like git revert). Non-destructive: creates a new "revert" entry preserving history. Use get_task_history first to find the change ID.',
|
|
1999
|
+
zodSchema: z.object({
|
|
2000
|
+
change_id: z.string().describe("Change ID to revert (from get_task_history)"),
|
|
2001
|
+
message: z.string().optional().describe("Custom message for the revert")
|
|
2002
|
+
}),
|
|
2003
|
+
handler: async (args, taskOps2) => {
|
|
2004
|
+
try {
|
|
2005
|
+
const revertChange = await taskOps2.revertChange(args.change_id, args.message);
|
|
2006
|
+
const task = await taskOps2.getTask(revertChange.task_id);
|
|
2007
|
+
return {
|
|
2008
|
+
content: [
|
|
2009
|
+
{
|
|
2010
|
+
type: "text",
|
|
2011
|
+
text: JSON.stringify(
|
|
2012
|
+
{
|
|
2013
|
+
success: true,
|
|
2014
|
+
reverted: {
|
|
2015
|
+
change_id: args.change_id,
|
|
2016
|
+
revert_id: revertChange.id,
|
|
2017
|
+
task_id: task.id,
|
|
2018
|
+
task_title: task.title,
|
|
2019
|
+
message: revertChange.message
|
|
2020
|
+
},
|
|
2021
|
+
hint: `View history: get_task_history(task_id="${task.id}")`
|
|
2022
|
+
},
|
|
2023
|
+
null,
|
|
2024
|
+
2
|
|
2025
|
+
)
|
|
2026
|
+
}
|
|
2027
|
+
]
|
|
2028
|
+
};
|
|
2029
|
+
} catch (error) {
|
|
2030
|
+
return {
|
|
2031
|
+
content: [
|
|
2032
|
+
{
|
|
2033
|
+
type: "text",
|
|
2034
|
+
text: JSON.stringify({ success: false, error: error.message }, null, 2)
|
|
2035
|
+
}
|
|
2036
|
+
]
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
},
|
|
2041
|
+
// pause_task - Like git stash
|
|
2042
|
+
{
|
|
2043
|
+
name: "pause_task",
|
|
2044
|
+
description: 'Pause a task (like git stash). Moves task to "paused" status with optional reason. Use this when you need to switch context but want to come back later.',
|
|
2045
|
+
zodSchema: z.object({
|
|
2046
|
+
task_id: z.string().describe("Task ID to pause"),
|
|
2047
|
+
reason: z.string().optional().describe("Why you are pausing this task")
|
|
2048
|
+
}),
|
|
2049
|
+
handler: async (args, taskOps2) => {
|
|
2050
|
+
try {
|
|
2051
|
+
const task = await taskOps2.pauseTask(args.task_id, args.reason);
|
|
2052
|
+
return {
|
|
2053
|
+
content: [
|
|
2054
|
+
{
|
|
2055
|
+
type: "text",
|
|
2056
|
+
text: JSON.stringify(
|
|
2057
|
+
{
|
|
2058
|
+
success: true,
|
|
2059
|
+
task: {
|
|
2060
|
+
id: task.id,
|
|
2061
|
+
title: task.title,
|
|
2062
|
+
status: "paused",
|
|
2063
|
+
reason: args.reason || null
|
|
2064
|
+
},
|
|
2065
|
+
hint: `Resume with: resume_task(task_id="${task.id}")`
|
|
2066
|
+
},
|
|
2067
|
+
null,
|
|
2068
|
+
2
|
|
2069
|
+
)
|
|
2070
|
+
}
|
|
2071
|
+
]
|
|
2072
|
+
};
|
|
2073
|
+
} catch (error) {
|
|
2074
|
+
return {
|
|
2075
|
+
content: [
|
|
2076
|
+
{
|
|
2077
|
+
type: "text",
|
|
2078
|
+
text: JSON.stringify({ success: false, error: error.message }, null, 2)
|
|
2079
|
+
}
|
|
2080
|
+
]
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
},
|
|
2085
|
+
// resume_task - Like git stash pop
|
|
2086
|
+
{
|
|
2087
|
+
name: "resume_task",
|
|
2088
|
+
description: 'Resume a paused task (like git stash pop). Moves task back to "vibing" status.',
|
|
2089
|
+
zodSchema: z.object({
|
|
2090
|
+
task_id: z.string().describe("Task ID to resume")
|
|
2091
|
+
}),
|
|
2092
|
+
handler: async (args, taskOps2) => {
|
|
2093
|
+
try {
|
|
2094
|
+
const task = await taskOps2.resumeTask(args.task_id);
|
|
2095
|
+
return {
|
|
2096
|
+
content: [
|
|
2097
|
+
{
|
|
2098
|
+
type: "text",
|
|
2099
|
+
text: JSON.stringify(
|
|
2100
|
+
{
|
|
2101
|
+
success: true,
|
|
2102
|
+
task: {
|
|
2103
|
+
id: task.id,
|
|
2104
|
+
title: task.title,
|
|
2105
|
+
status: "vibing",
|
|
2106
|
+
context_notes: task.context_notes
|
|
2107
|
+
},
|
|
2108
|
+
message: "Task resumed! Continue where you left off."
|
|
2109
|
+
},
|
|
2110
|
+
null,
|
|
2111
|
+
2
|
|
2112
|
+
)
|
|
2113
|
+
}
|
|
2114
|
+
]
|
|
2115
|
+
};
|
|
2116
|
+
} catch (error) {
|
|
2117
|
+
return {
|
|
2118
|
+
content: [
|
|
2119
|
+
{
|
|
2120
|
+
type: "text",
|
|
2121
|
+
text: JSON.stringify({ success: false, error: error.message }, null, 2)
|
|
2122
|
+
}
|
|
2123
|
+
]
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
},
|
|
2128
|
+
// get_paused_tasks - List all paused tasks
|
|
2129
|
+
{
|
|
2130
|
+
name: "get_paused_tasks",
|
|
2131
|
+
description: "Get all paused tasks. Shows tasks that have been paused (stashed) for later.",
|
|
2132
|
+
zodSchema: z.object({}),
|
|
2133
|
+
handler: async (_args, taskOps2) => {
|
|
2134
|
+
try {
|
|
2135
|
+
const allTasks = await taskOps2.getTasks("all");
|
|
2136
|
+
const pausedTasks = allTasks.filter((t) => t.status === "paused");
|
|
2137
|
+
return {
|
|
2138
|
+
content: [
|
|
2139
|
+
{
|
|
2140
|
+
type: "text",
|
|
2141
|
+
text: JSON.stringify(
|
|
2142
|
+
{
|
|
2143
|
+
success: true,
|
|
2144
|
+
paused_tasks: pausedTasks.map((t) => ({
|
|
2145
|
+
id: t.id,
|
|
2146
|
+
title: t.title,
|
|
2147
|
+
context_notes: t.context_notes,
|
|
2148
|
+
priority: t.priority
|
|
2149
|
+
})),
|
|
2150
|
+
count: pausedTasks.length,
|
|
2151
|
+
hint: pausedTasks.length > 0 ? `Resume with: resume_task(task_id="...")` : "No paused tasks."
|
|
2152
|
+
},
|
|
2153
|
+
null,
|
|
2154
|
+
2
|
|
2155
|
+
)
|
|
2156
|
+
}
|
|
2157
|
+
]
|
|
2158
|
+
};
|
|
2159
|
+
} catch (error) {
|
|
2160
|
+
return {
|
|
2161
|
+
content: [
|
|
2162
|
+
{
|
|
2163
|
+
type: "text",
|
|
2164
|
+
text: JSON.stringify({ success: false, error: error.message }, null, 2)
|
|
2165
|
+
}
|
|
2166
|
+
]
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
},
|
|
2171
|
+
// search_changes - Search across all task changes
|
|
2172
|
+
{
|
|
2173
|
+
name: "search_changes",
|
|
2174
|
+
description: "Search task changes by message (like git log --grep). Find when something was changed across all tasks.",
|
|
2175
|
+
zodSchema: z.object({
|
|
2176
|
+
query: z.string().describe("Search query to match against change messages"),
|
|
2177
|
+
limit: z.number().default(20).describe("Maximum results")
|
|
2178
|
+
}),
|
|
2179
|
+
handler: async (args, taskOps2) => {
|
|
2180
|
+
try {
|
|
2181
|
+
const changes = await taskOps2.searchChanges(args.query, args.limit);
|
|
2182
|
+
return {
|
|
2183
|
+
content: [
|
|
2184
|
+
{
|
|
2185
|
+
type: "text",
|
|
2186
|
+
text: JSON.stringify(
|
|
2187
|
+
{
|
|
2188
|
+
success: true,
|
|
2189
|
+
query: args.query,
|
|
2190
|
+
changes: changes.map((c) => ({
|
|
2191
|
+
id: c.id,
|
|
2192
|
+
task_id: c.task_id,
|
|
2193
|
+
change_type: c.change_type,
|
|
2194
|
+
message: c.message,
|
|
2195
|
+
actor_type: c.actor_type,
|
|
2196
|
+
created_at: c.created_at
|
|
2197
|
+
})),
|
|
2198
|
+
count: changes.length
|
|
2199
|
+
},
|
|
2200
|
+
null,
|
|
2201
|
+
2
|
|
2202
|
+
)
|
|
2203
|
+
}
|
|
2204
|
+
]
|
|
2205
|
+
};
|
|
2206
|
+
} catch (error) {
|
|
2207
|
+
return {
|
|
2208
|
+
content: [
|
|
2209
|
+
{
|
|
2210
|
+
type: "text",
|
|
2211
|
+
text: JSON.stringify({ success: false, error: error.message }, null, 2)
|
|
2212
|
+
}
|
|
2213
|
+
]
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
1324
2217
|
}
|
|
1325
2218
|
];
|
|
2219
|
+
return toolsWithZod.map((tool) => ({
|
|
2220
|
+
name: tool.name,
|
|
2221
|
+
description: tool.description,
|
|
2222
|
+
inputSchema: toJsonSchema(tool.zodSchema),
|
|
2223
|
+
handler: tool.handler
|
|
2224
|
+
}));
|
|
1326
2225
|
}
|
|
1327
2226
|
function summarizeErrorsLocal(logText, maxLines = 100) {
|
|
1328
2227
|
const errorTypePatterns = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibetasks/mcp-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "VibeTasks MCP Server for Claude Code, Cursor, and AI coding tools. Status-based task management: todo → vibing → done.",
|
|
5
5
|
"author": "Vyas",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,14 +45,15 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
48
|
-
"@vibetasks/core": "^0.5.
|
|
49
|
-
"@vibetasks/shared": "
|
|
50
|
-
"zod": "^3.22.0"
|
|
48
|
+
"@vibetasks/core": "^0.5.8",
|
|
49
|
+
"@vibetasks/shared": "^1.4.5",
|
|
50
|
+
"zod": "^3.22.0",
|
|
51
|
+
"zod-to-json-schema": "^3.25.1"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"@types/node": "^20.0.0",
|
|
54
|
-
"tsx": "^4.7.0",
|
|
55
55
|
"tsup": "^8.0.0",
|
|
56
|
+
"tsx": "^4.7.0",
|
|
56
57
|
"typescript": "^5.3.3"
|
|
57
58
|
}
|
|
58
59
|
}
|