@vibetasks/mcp-server 0.1.0

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 ADDED
@@ -0,0 +1,565 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import {
7
+ CallToolRequestSchema,
8
+ ListToolsRequestSchema,
9
+ ListResourcesRequestSchema,
10
+ ReadResourceRequestSchema
11
+ } from "@modelcontextprotocol/sdk/types.js";
12
+ import { AuthManager, TaskOperations } from "@vibetasks/core";
13
+
14
+ // src/tools/index.ts
15
+ import { z } from "zod";
16
+ function setupTools(taskOps) {
17
+ return [
18
+ // create_task
19
+ {
20
+ name: "create_task",
21
+ description: "Create a new task in TaskFlow. Can optionally be a subtask by providing parent_task_id.",
22
+ inputSchema: z.object({
23
+ title: z.string().describe("Task title (required)"),
24
+ notes: z.string().optional().describe("Task notes in markdown"),
25
+ due_date: z.string().optional().describe("Due date (ISO 8601 format)"),
26
+ priority: z.enum(["none", "low", "medium", "high"]).default("none").describe("Task priority"),
27
+ tags: z.array(z.string()).optional().describe("Tag names to attach"),
28
+ parent_task_id: z.string().optional().describe("Parent task ID to create this as a subtask")
29
+ }),
30
+ handler: async (args, taskOps2) => {
31
+ let tagIds = [];
32
+ if (args.tags && args.tags.length > 0) {
33
+ for (const tagName of args.tags) {
34
+ const tag = await taskOps2.findOrCreateTag(tagName);
35
+ tagIds.push(tag.id);
36
+ }
37
+ }
38
+ const task = await taskOps2.createTask({
39
+ title: args.title,
40
+ notes: args.notes,
41
+ notes_format: "markdown",
42
+ due_date: args.due_date,
43
+ priority: args.priority,
44
+ parent_task_id: args.parent_task_id
45
+ });
46
+ if (tagIds.length > 0) {
47
+ await taskOps2.linkTaskTags(task.id, tagIds);
48
+ }
49
+ return {
50
+ content: [
51
+ {
52
+ type: "text",
53
+ text: JSON.stringify(
54
+ {
55
+ success: true,
56
+ task: {
57
+ id: task.id,
58
+ title: task.title,
59
+ priority: task.priority,
60
+ due_date: task.due_date,
61
+ parent_task_id: task.parent_task_id,
62
+ created_at: task.created_at
63
+ }
64
+ },
65
+ null,
66
+ 2
67
+ )
68
+ }
69
+ ]
70
+ };
71
+ }
72
+ },
73
+ // get_tasks
74
+ {
75
+ name: "get_tasks",
76
+ description: "Get tasks by filter",
77
+ inputSchema: z.object({
78
+ filter: z.enum(["all", "today", "upcoming", "completed"]).default("all").describe("Task filter")
79
+ }),
80
+ handler: async (args, taskOps2) => {
81
+ const tasks = await taskOps2.getTasks(args.filter);
82
+ return {
83
+ content: [
84
+ {
85
+ type: "text",
86
+ text: JSON.stringify(
87
+ {
88
+ success: true,
89
+ tasks: tasks.map((t) => ({
90
+ id: t.id,
91
+ title: t.title,
92
+ priority: t.priority,
93
+ completed: t.completed,
94
+ due_date: t.due_date,
95
+ tags: t.tags?.map((tag) => tag.name) || []
96
+ })),
97
+ count: tasks.length
98
+ },
99
+ null,
100
+ 2
101
+ )
102
+ }
103
+ ]
104
+ };
105
+ }
106
+ },
107
+ // update_task
108
+ {
109
+ name: "update_task",
110
+ description: "Update an existing task",
111
+ inputSchema: z.object({
112
+ task_id: z.string().describe("Task ID"),
113
+ title: z.string().optional().describe("New title"),
114
+ notes: z.string().optional().describe("New notes"),
115
+ due_date: z.string().optional().describe("New due date (ISO 8601)"),
116
+ priority: z.enum(["none", "low", "medium", "high"]).optional().describe("New priority"),
117
+ completed: z.boolean().optional().describe("Completion status")
118
+ }),
119
+ handler: async (args, taskOps2) => {
120
+ const updates = {};
121
+ if (args.title !== void 0) updates.title = args.title;
122
+ if (args.notes !== void 0) updates.notes = args.notes;
123
+ if (args.due_date !== void 0) updates.due_date = args.due_date;
124
+ if (args.priority !== void 0) updates.priority = args.priority;
125
+ if (args.completed !== void 0) updates.completed = args.completed;
126
+ const task = await taskOps2.updateTask(args.task_id, updates);
127
+ return {
128
+ content: [
129
+ {
130
+ type: "text",
131
+ text: JSON.stringify(
132
+ {
133
+ success: true,
134
+ task: {
135
+ id: task.id,
136
+ title: task.title,
137
+ completed: task.completed,
138
+ priority: task.priority
139
+ }
140
+ },
141
+ null,
142
+ 2
143
+ )
144
+ }
145
+ ]
146
+ };
147
+ }
148
+ },
149
+ // complete_task
150
+ {
151
+ name: "complete_task",
152
+ description: "Mark a task as complete",
153
+ inputSchema: z.object({
154
+ task_id: z.string().describe("Task ID")
155
+ }),
156
+ handler: async (args, taskOps2) => {
157
+ const task = await taskOps2.completeTask(args.task_id);
158
+ return {
159
+ content: [
160
+ {
161
+ type: "text",
162
+ text: JSON.stringify(
163
+ {
164
+ success: true,
165
+ task: {
166
+ id: task.id,
167
+ title: task.title,
168
+ completed: true,
169
+ completed_at: task.completed_at
170
+ }
171
+ },
172
+ null,
173
+ 2
174
+ )
175
+ }
176
+ ]
177
+ };
178
+ }
179
+ },
180
+ // delete_task
181
+ {
182
+ name: "delete_task",
183
+ description: "Delete a task",
184
+ inputSchema: z.object({
185
+ task_id: z.string().describe("Task ID")
186
+ }),
187
+ handler: async (args, taskOps2) => {
188
+ await taskOps2.deleteTask(args.task_id);
189
+ return {
190
+ content: [
191
+ {
192
+ type: "text",
193
+ text: JSON.stringify(
194
+ {
195
+ success: true,
196
+ message: "Task deleted successfully"
197
+ },
198
+ null,
199
+ 2
200
+ )
201
+ }
202
+ ]
203
+ };
204
+ }
205
+ },
206
+ // search_tasks
207
+ {
208
+ name: "search_tasks",
209
+ description: "Search tasks by title",
210
+ inputSchema: z.object({
211
+ query: z.string().describe("Search query"),
212
+ limit: z.number().default(20).describe("Maximum results")
213
+ }),
214
+ handler: async (args, taskOps2) => {
215
+ const tasks = await taskOps2.searchTasks(args.query, args.limit);
216
+ return {
217
+ content: [
218
+ {
219
+ type: "text",
220
+ text: JSON.stringify(
221
+ {
222
+ success: true,
223
+ tasks: tasks.map((t) => ({
224
+ id: t.id,
225
+ title: t.title,
226
+ priority: t.priority,
227
+ due_date: t.due_date,
228
+ tags: t.tags?.map((tag) => tag.name) || []
229
+ })),
230
+ count: tasks.length
231
+ },
232
+ null,
233
+ 2
234
+ )
235
+ }
236
+ ]
237
+ };
238
+ }
239
+ },
240
+ // create_task_with_subtasks
241
+ {
242
+ name: "create_task_with_subtasks",
243
+ 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.",
244
+ inputSchema: z.object({
245
+ title: z.string().describe("Parent task title (the overall goal)"),
246
+ subtasks: z.array(z.string()).describe("Array of subtask titles (one for each todo item)"),
247
+ notes: z.string().optional().describe("Notes for parent task"),
248
+ due_date: z.string().optional().describe("Due date for parent task (ISO 8601 format)"),
249
+ priority: z.enum(["none", "low", "medium", "high"]).default("none").describe("Priority for parent task"),
250
+ tags: z.array(z.string()).optional().describe("Tag names to attach to parent task")
251
+ }),
252
+ handler: async (args, taskOps2) => {
253
+ let tagIds = [];
254
+ if (args.tags && args.tags.length > 0) {
255
+ for (const tagName of args.tags) {
256
+ const tag = await taskOps2.findOrCreateTag(tagName);
257
+ tagIds.push(tag.id);
258
+ }
259
+ }
260
+ const parentTask = await taskOps2.createTask({
261
+ title: args.title,
262
+ notes: args.notes,
263
+ notes_format: "markdown",
264
+ due_date: args.due_date,
265
+ priority: args.priority
266
+ });
267
+ if (tagIds.length > 0) {
268
+ await taskOps2.linkTaskTags(parentTask.id, tagIds);
269
+ }
270
+ const subtasks = [];
271
+ for (const subtaskTitle of args.subtasks) {
272
+ const subtask = await taskOps2.createTask({
273
+ title: subtaskTitle,
274
+ notes_format: "markdown",
275
+ priority: "none",
276
+ parent_task_id: parentTask.id
277
+ });
278
+ subtasks.push(subtask);
279
+ }
280
+ return {
281
+ content: [
282
+ {
283
+ type: "text",
284
+ text: JSON.stringify(
285
+ {
286
+ success: true,
287
+ parent_task: {
288
+ id: parentTask.id,
289
+ title: parentTask.title,
290
+ priority: parentTask.priority,
291
+ subtask_count: subtasks.length
292
+ },
293
+ subtasks: subtasks.map((s) => ({
294
+ id: s.id,
295
+ title: s.title
296
+ }))
297
+ },
298
+ null,
299
+ 2
300
+ )
301
+ }
302
+ ]
303
+ };
304
+ }
305
+ },
306
+ // log_ai_session
307
+ {
308
+ name: "log_ai_session",
309
+ description: "Manually log an AI session as a completed task",
310
+ inputSchema: z.object({
311
+ summary: z.string().describe("What was accomplished"),
312
+ files: z.array(z.string()).optional().describe("Files changed"),
313
+ duration_minutes: z.number().optional().describe("Session duration")
314
+ }),
315
+ handler: async (args, taskOps2) => {
316
+ const notes = `# AI Session Summary
317
+
318
+ ${args.summary}
319
+
320
+ ${args.files && args.files.length > 0 ? `## Files Changed
321
+ ${args.files.map((f) => `- ${f}`).join("\n")}` : ""}
322
+
323
+ ${args.duration_minutes ? `Duration: ${args.duration_minutes} minutes` : ""}
324
+
325
+ Generated by TaskFlow MCP Server`;
326
+ const task = await taskOps2.createTask({
327
+ title: `AI Session: ${args.summary.substring(0, 50)}${args.summary.length > 50 ? "..." : ""}`,
328
+ notes,
329
+ notes_format: "markdown",
330
+ completed: true,
331
+ priority: "none"
332
+ });
333
+ return {
334
+ content: [
335
+ {
336
+ type: "text",
337
+ text: JSON.stringify(
338
+ {
339
+ success: true,
340
+ task: {
341
+ id: task.id,
342
+ title: task.title,
343
+ completed: true
344
+ }
345
+ },
346
+ null,
347
+ 2
348
+ )
349
+ }
350
+ ]
351
+ };
352
+ }
353
+ }
354
+ ];
355
+ }
356
+
357
+ // src/resources/index.ts
358
+ function setupResources(taskOps) {
359
+ return [
360
+ // Active tasks resource
361
+ {
362
+ uri: "taskflow://tasks/active",
363
+ name: "Active Tasks",
364
+ description: "All incomplete tasks",
365
+ mimeType: "application/json",
366
+ handler: async (taskOps2) => {
367
+ const tasks = await taskOps2.getTasks("all");
368
+ return {
369
+ contents: [
370
+ {
371
+ uri: "taskflow://tasks/active",
372
+ mimeType: "application/json",
373
+ text: JSON.stringify(
374
+ {
375
+ tasks: tasks.map((t) => ({
376
+ id: t.id,
377
+ title: t.title,
378
+ priority: t.priority,
379
+ due_date: t.due_date,
380
+ tags: t.tags?.map((tag) => tag.name) || []
381
+ })),
382
+ count: tasks.length
383
+ },
384
+ null,
385
+ 2
386
+ )
387
+ }
388
+ ]
389
+ };
390
+ }
391
+ },
392
+ // Today's tasks resource
393
+ {
394
+ uri: "taskflow://tasks/today",
395
+ name: "Today's Tasks",
396
+ description: "Tasks due today",
397
+ mimeType: "application/json",
398
+ handler: async (taskOps2) => {
399
+ const tasks = await taskOps2.getTasks("today");
400
+ return {
401
+ contents: [
402
+ {
403
+ uri: "taskflow://tasks/today",
404
+ mimeType: "application/json",
405
+ text: JSON.stringify(
406
+ {
407
+ tasks: tasks.map((t) => ({
408
+ id: t.id,
409
+ title: t.title,
410
+ priority: t.priority,
411
+ tags: t.tags?.map((tag) => tag.name) || []
412
+ })),
413
+ count: tasks.length,
414
+ date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
415
+ },
416
+ null,
417
+ 2
418
+ )
419
+ }
420
+ ]
421
+ };
422
+ }
423
+ },
424
+ // Upcoming tasks resource
425
+ {
426
+ uri: "taskflow://tasks/upcoming",
427
+ name: "Upcoming Tasks",
428
+ description: "Tasks due in the future",
429
+ mimeType: "application/json",
430
+ handler: async (taskOps2) => {
431
+ const tasks = await taskOps2.getTasks("upcoming");
432
+ return {
433
+ contents: [
434
+ {
435
+ uri: "taskflow://tasks/upcoming",
436
+ mimeType: "application/json",
437
+ text: JSON.stringify(
438
+ {
439
+ tasks: tasks.map((t) => ({
440
+ id: t.id,
441
+ title: t.title,
442
+ priority: t.priority,
443
+ due_date: t.due_date,
444
+ tags: t.tags?.map((tag) => tag.name) || []
445
+ })),
446
+ count: tasks.length
447
+ },
448
+ null,
449
+ 2
450
+ )
451
+ }
452
+ ]
453
+ };
454
+ }
455
+ }
456
+ ];
457
+ }
458
+
459
+ // src/index.ts
460
+ var hookType = process.env.CLAUDE_HOOK_TYPE;
461
+ if (hookType === "SessionStart") {
462
+ const { handleSessionStart } = await import("./session-start-OIAJ7YIL.js");
463
+ await handleSessionStart();
464
+ process.exit(0);
465
+ } else if (hookType === "SessionEnd" || hookType === "Stop") {
466
+ const { handleSessionEnd } = await import("./session-end-6BUUHSB7.js");
467
+ await handleSessionEnd();
468
+ process.exit(0);
469
+ }
470
+ async function main() {
471
+ try {
472
+ const authManager = new AuthManager();
473
+ const supabaseUrl = process.env.TASKFLOW_SUPABASE_URL || await authManager.getConfig("supabase_url") || "https://cbkkztbcoitrfcleghfd.supabase.co";
474
+ const supabaseKey = process.env.TASKFLOW_SUPABASE_KEY || await authManager.getConfig("supabase_key") || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNia2t6dGJjb2l0cmZjbGVnaGZkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njc3NTc0MjgsImV4cCI6MjA4MzMzMzQyOH0.G7ILx-nntP0NbxO1gKt5yASb7nt7OmpJ8qtykeGYbQA";
475
+ const accessToken = await authManager.getAccessToken();
476
+ if (!accessToken) {
477
+ console.error("ERROR: Not authenticated.", { severity: "error" });
478
+ console.error("Run: taskflow login", { severity: "error" });
479
+ process.exit(1);
480
+ }
481
+ if (!accessToken) {
482
+ console.error("ERROR: Not authenticated.", { severity: "error" });
483
+ console.error("Run: taskflow login", { severity: "error" });
484
+ process.exit(1);
485
+ }
486
+ const taskOps = await TaskOperations.fromAuthManager(authManager);
487
+ const server = new Server(
488
+ {
489
+ name: "taskflow-mcp-server",
490
+ version: "1.0.0"
491
+ },
492
+ {
493
+ capabilities: {
494
+ tools: {},
495
+ resources: {}
496
+ }
497
+ }
498
+ );
499
+ const tools = setupTools(taskOps);
500
+ const resources = setupResources(taskOps);
501
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
502
+ return {
503
+ tools: tools.map((t) => ({
504
+ name: t.name,
505
+ description: t.description,
506
+ inputSchema: t.inputSchema
507
+ }))
508
+ };
509
+ });
510
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
511
+ const tool = tools.find((t) => t.name === request.params.name);
512
+ if (!tool) {
513
+ throw new Error(`Unknown tool: ${request.params.name}`);
514
+ }
515
+ try {
516
+ return await tool.handler(request.params.arguments || {}, taskOps);
517
+ } catch (error) {
518
+ return {
519
+ content: [
520
+ {
521
+ type: "text",
522
+ text: JSON.stringify(
523
+ {
524
+ success: false,
525
+ error: error.message
526
+ },
527
+ null,
528
+ 2
529
+ )
530
+ }
531
+ ],
532
+ isError: true
533
+ };
534
+ }
535
+ });
536
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
537
+ return {
538
+ resources: resources.map((r) => ({
539
+ uri: r.uri,
540
+ name: r.name,
541
+ description: r.description,
542
+ mimeType: r.mimeType
543
+ }))
544
+ };
545
+ });
546
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
547
+ const resource = resources.find((r) => r.uri === request.params.uri);
548
+ if (!resource) {
549
+ throw new Error(`Unknown resource: ${request.params.uri}`);
550
+ }
551
+ try {
552
+ return await resource.handler(taskOps);
553
+ } catch (error) {
554
+ throw new Error(`Failed to read resource: ${error.message}`);
555
+ }
556
+ });
557
+ const transport = new StdioServerTransport();
558
+ await server.connect(transport);
559
+ console.error("TaskFlow MCP server started", { severity: "info" });
560
+ } catch (error) {
561
+ console.error("Fatal error:", error, { severity: "error" });
562
+ process.exit(1);
563
+ }
564
+ }
565
+ main();
@@ -0,0 +1,56 @@
1
+ // src/hooks/session-end.ts
2
+ import { AuthManager, TaskOperations } from "@vibetasks/core";
3
+ async function handleSessionEnd() {
4
+ try {
5
+ const authManager = new AuthManager();
6
+ const isAuth = await authManager.isAuthenticated();
7
+ if (!isAuth) {
8
+ return;
9
+ }
10
+ const metadataJson = process.env.CLAUDE_SESSION_METADATA || "{}";
11
+ const metadata = JSON.parse(metadataJson);
12
+ const filesEdited = metadata.filesEdited || [];
13
+ const duration = metadata.duration || 0;
14
+ const aiActions = metadata.aiActions || [];
15
+ const shouldLog = filesEdited.length > 0 && duration >= 6e4 && aiActions.length > 0;
16
+ if (!shouldLog) {
17
+ console.error("TaskFlow: Session too short, not logging");
18
+ return;
19
+ }
20
+ const taskOps = await TaskOperations.fromAuthManager(authManager);
21
+ const durationMinutes = Math.round(duration / 6e4);
22
+ const primaryAction = aiActions[0] || "Code changes";
23
+ const summary = `# AI Session Completed
24
+
25
+ ## Summary
26
+ ${primaryAction}
27
+
28
+ ## Duration
29
+ ${durationMinutes} minutes
30
+
31
+ ## Files Edited (${filesEdited.length})
32
+ ${filesEdited.slice(0, 15).map((f) => `- ${f}`).join("\n")}
33
+ ${filesEdited.length > 15 ? `
34
+ ...and ${filesEdited.length - 15} more files` : ""}
35
+
36
+ ## Actions
37
+ ${aiActions.map((a) => `- ${a}`).join("\n")}
38
+
39
+ ---
40
+ *Generated by TaskFlow MCP Server*
41
+ `.trim();
42
+ await taskOps.createTask({
43
+ title: `AI Session: ${primaryAction}`,
44
+ notes: summary,
45
+ notes_format: "markdown",
46
+ completed: true,
47
+ priority: "none"
48
+ });
49
+ console.error("TaskFlow: Logged AI session as completed task");
50
+ } catch (error) {
51
+ console.error("TaskFlow SessionEnd hook error:", error.message);
52
+ }
53
+ }
54
+ export {
55
+ handleSessionEnd
56
+ };
@@ -0,0 +1,49 @@
1
+ // src/hooks/session-start.ts
2
+ import { AuthManager, TaskOperations } from "@vibetasks/core";
3
+ async function handleSessionStart() {
4
+ try {
5
+ const authManager = new AuthManager();
6
+ const isAuth = await authManager.isAuthenticated();
7
+ if (!isAuth) {
8
+ console.log(JSON.stringify({ additionalContext: "" }));
9
+ return;
10
+ }
11
+ const taskOps = await TaskOperations.fromAuthManager(authManager);
12
+ const todayTasks = await taskOps.getTasks("today");
13
+ const activeTasks = await taskOps.getTasks("all");
14
+ const context = `# TaskFlow - Your Tasks
15
+
16
+ ## Today's Tasks (${todayTasks.length})
17
+ ${todayTasks.length === 0 ? "No tasks due today." : todayTasks.map((t) => {
18
+ const priority = t.priority && t.priority !== "none" ? ` [${t.priority.toUpperCase()}]` : "";
19
+ const tags = t.tags && t.tags.length > 0 ? ` #${t.tags.map((tag) => tag.name).join(" #")}` : "";
20
+ return `- [ ] ${t.title}${priority}${tags}`;
21
+ }).join("\n")}
22
+
23
+ ## All Active Tasks (${activeTasks.length})
24
+ ${activeTasks.length === 0 ? "No active tasks." : activeTasks.slice(0, 10).map((t) => {
25
+ const priority = t.priority && t.priority !== "none" ? ` [${t.priority.toUpperCase()}]` : "";
26
+ const dueDate = t.due_date ? ` (Due: ${t.due_date.split("T")[0]})` : "";
27
+ return `- [ ] ${t.title}${priority}${dueDate}`;
28
+ }).join("\n")}${activeTasks.length > 10 ? `
29
+ ...and ${activeTasks.length - 10} more` : ""}
30
+
31
+ ---
32
+ You can manage tasks using these MCP tools:
33
+ - create_task: Add new tasks
34
+ - get_tasks: View tasks by filter
35
+ - complete_task: Mark tasks done
36
+ - search_tasks: Find tasks
37
+ - update_task: Modify tasks
38
+ - delete_task: Remove tasks
39
+ - log_ai_session: Log what we accomplish together
40
+ `.trim();
41
+ console.log(JSON.stringify({ additionalContext: context }));
42
+ } catch (error) {
43
+ console.error("TaskFlow SessionStart hook error:", error.message);
44
+ console.log(JSON.stringify({ additionalContext: "" }));
45
+ }
46
+ }
47
+ export {
48
+ handleSessionStart
49
+ };
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@vibetasks/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "VibeTasks MCP Server for Claude Code and other AI coding tools",
5
+ "type": "module",
6
+ "bin": {
7
+ "vibetasks-mcp": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "tsx src/index.ts",
11
+ "build": "tsup src/index.ts --format esm --clean --outDir dist --shims",
12
+ "start": "node dist/index.js",
13
+ "typecheck": "tsc --noEmit"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^0.5.0",
20
+ "@vibetasks/core": "^0.1.0",
21
+ "zod": "^3.22.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.0.0",
25
+ "tsx": "^4.7.0",
26
+ "tsup": "^8.0.0",
27
+ "typescript": "^5.3.3"
28
+ }
29
+ }