@vibetasks/mcp-server 0.2.1 → 0.3.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 +615 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -303,6 +303,171 @@ function setupTools(taskOps) {
|
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
305
|
},
|
|
306
|
+
// start_vibing
|
|
307
|
+
{
|
|
308
|
+
name: "start_vibing",
|
|
309
|
+
description: 'Start vibing on a task (move to "vibing" status). Max 3 tasks vibing at once - research shows more causes 40% productivity loss.',
|
|
310
|
+
inputSchema: z.object({
|
|
311
|
+
task_id: z.string().describe("Task ID to start vibing on")
|
|
312
|
+
}),
|
|
313
|
+
handler: async (args, taskOps2) => {
|
|
314
|
+
const allTasks = await taskOps2.getTasks("all");
|
|
315
|
+
const vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
316
|
+
const WIP_LIMIT = 3;
|
|
317
|
+
if (vibingTasks.length >= WIP_LIMIT) {
|
|
318
|
+
return {
|
|
319
|
+
content: [
|
|
320
|
+
{
|
|
321
|
+
type: "text",
|
|
322
|
+
text: JSON.stringify(
|
|
323
|
+
{
|
|
324
|
+
success: false,
|
|
325
|
+
error: `WIP limit reached! You have ${vibingTasks.length} tasks vibing. Complete one before starting another.`,
|
|
326
|
+
vibing_tasks: vibingTasks.map((t) => ({ id: t.id, title: t.title })),
|
|
327
|
+
wip_limit: WIP_LIMIT
|
|
328
|
+
},
|
|
329
|
+
null,
|
|
330
|
+
2
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
]
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const task = await taskOps2.updateTask(args.task_id, { status: "vibing" });
|
|
337
|
+
return {
|
|
338
|
+
content: [
|
|
339
|
+
{
|
|
340
|
+
type: "text",
|
|
341
|
+
text: JSON.stringify(
|
|
342
|
+
{
|
|
343
|
+
success: true,
|
|
344
|
+
task: {
|
|
345
|
+
id: task.id,
|
|
346
|
+
title: task.title,
|
|
347
|
+
status: "vibing"
|
|
348
|
+
},
|
|
349
|
+
wip_count: vibingTasks.length + 1,
|
|
350
|
+
wip_limit: WIP_LIMIT
|
|
351
|
+
},
|
|
352
|
+
null,
|
|
353
|
+
2
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
]
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
// get_vibing_tasks
|
|
361
|
+
{
|
|
362
|
+
name: "get_vibing_tasks",
|
|
363
|
+
description: 'Get all tasks currently in "vibing" status. Use this to check what the user is actively working on.',
|
|
364
|
+
inputSchema: z.object({}),
|
|
365
|
+
handler: async (args, taskOps2) => {
|
|
366
|
+
const allTasks = await taskOps2.getTasks("all");
|
|
367
|
+
const vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
368
|
+
const WIP_LIMIT = 3;
|
|
369
|
+
return {
|
|
370
|
+
content: [
|
|
371
|
+
{
|
|
372
|
+
type: "text",
|
|
373
|
+
text: JSON.stringify(
|
|
374
|
+
{
|
|
375
|
+
success: true,
|
|
376
|
+
vibing_tasks: vibingTasks.map((t) => ({
|
|
377
|
+
id: t.id,
|
|
378
|
+
title: t.title,
|
|
379
|
+
context_notes: t.context_notes,
|
|
380
|
+
priority: t.priority,
|
|
381
|
+
due_date: t.due_date
|
|
382
|
+
})),
|
|
383
|
+
count: vibingTasks.length,
|
|
384
|
+
wip_limit: WIP_LIMIT,
|
|
385
|
+
at_limit: vibingTasks.length >= WIP_LIMIT
|
|
386
|
+
},
|
|
387
|
+
null,
|
|
388
|
+
2
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
]
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
// set_context_notes
|
|
396
|
+
{
|
|
397
|
+
name: "set_context_notes",
|
|
398
|
+
description: 'Save "where I left off" notes for a task. Use this before pausing work or ending a session to capture context for later.',
|
|
399
|
+
inputSchema: z.object({
|
|
400
|
+
task_id: z.string().describe("Task ID"),
|
|
401
|
+
context_notes: z.string().describe("Where you left off - file, line, next step, blockers")
|
|
402
|
+
}),
|
|
403
|
+
handler: async (args, taskOps2) => {
|
|
404
|
+
const task = await taskOps2.updateTask(args.task_id, {
|
|
405
|
+
context_notes: args.context_notes
|
|
406
|
+
});
|
|
407
|
+
return {
|
|
408
|
+
content: [
|
|
409
|
+
{
|
|
410
|
+
type: "text",
|
|
411
|
+
text: JSON.stringify(
|
|
412
|
+
{
|
|
413
|
+
success: true,
|
|
414
|
+
task: {
|
|
415
|
+
id: task.id,
|
|
416
|
+
title: task.title,
|
|
417
|
+
context_notes: task.context_notes
|
|
418
|
+
},
|
|
419
|
+
message: "Context notes saved. You can resume seamlessly later."
|
|
420
|
+
},
|
|
421
|
+
null,
|
|
422
|
+
2
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
// list_tasks (with status filter)
|
|
430
|
+
{
|
|
431
|
+
name: "list_tasks",
|
|
432
|
+
description: "List tasks filtered by status (todo, vibing, done) or all tasks",
|
|
433
|
+
inputSchema: z.object({
|
|
434
|
+
status: z.enum(["todo", "vibing", "done", "all"]).default("all").describe("Filter by status"),
|
|
435
|
+
limit: z.number().default(50).describe("Maximum results")
|
|
436
|
+
}),
|
|
437
|
+
handler: async (args, taskOps2) => {
|
|
438
|
+
const allTasks = await taskOps2.getTasks("all");
|
|
439
|
+
let tasks = allTasks;
|
|
440
|
+
if (args.status !== "all") {
|
|
441
|
+
tasks = allTasks.filter((t) => t.status === args.status);
|
|
442
|
+
}
|
|
443
|
+
tasks = tasks.slice(0, args.limit);
|
|
444
|
+
return {
|
|
445
|
+
content: [
|
|
446
|
+
{
|
|
447
|
+
type: "text",
|
|
448
|
+
text: JSON.stringify(
|
|
449
|
+
{
|
|
450
|
+
success: true,
|
|
451
|
+
tasks: tasks.map((t) => ({
|
|
452
|
+
id: t.id,
|
|
453
|
+
title: t.title,
|
|
454
|
+
status: t.status,
|
|
455
|
+
priority: t.priority,
|
|
456
|
+
due_date: t.due_date,
|
|
457
|
+
context_notes: t.context_notes,
|
|
458
|
+
tags: t.tags?.map((tag) => tag.name) || []
|
|
459
|
+
})),
|
|
460
|
+
count: tasks.length,
|
|
461
|
+
filter: args.status
|
|
462
|
+
},
|
|
463
|
+
null,
|
|
464
|
+
2
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
]
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
},
|
|
306
471
|
// log_ai_session
|
|
307
472
|
{
|
|
308
473
|
name: "log_ai_session",
|
|
@@ -350,9 +515,459 @@ Generated by TaskFlow MCP Server`;
|
|
|
350
515
|
]
|
|
351
516
|
};
|
|
352
517
|
}
|
|
518
|
+
},
|
|
519
|
+
// archive_task
|
|
520
|
+
{
|
|
521
|
+
name: "archive_task",
|
|
522
|
+
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.",
|
|
523
|
+
inputSchema: z.object({
|
|
524
|
+
task_id: z.string().describe("Task ID to archive")
|
|
525
|
+
}),
|
|
526
|
+
handler: async (args, taskOps2) => {
|
|
527
|
+
const task = await taskOps2.archiveTask(args.task_id);
|
|
528
|
+
return {
|
|
529
|
+
content: [
|
|
530
|
+
{
|
|
531
|
+
type: "text",
|
|
532
|
+
text: JSON.stringify(
|
|
533
|
+
{
|
|
534
|
+
success: true,
|
|
535
|
+
task: {
|
|
536
|
+
id: task.id,
|
|
537
|
+
title: task.title,
|
|
538
|
+
status: "archived",
|
|
539
|
+
archived_at: task.archived_at
|
|
540
|
+
},
|
|
541
|
+
message: "Task archived successfully. Use get_archived_tasks to view archived tasks."
|
|
542
|
+
},
|
|
543
|
+
null,
|
|
544
|
+
2
|
|
545
|
+
)
|
|
546
|
+
}
|
|
547
|
+
]
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
// get_archived_tasks
|
|
552
|
+
{
|
|
553
|
+
name: "get_archived_tasks",
|
|
554
|
+
description: "Get all archived tasks. Returns tasks that have been archived, sorted by archive date (most recent first).",
|
|
555
|
+
inputSchema: z.object({
|
|
556
|
+
limit: z.number().default(50).describe("Maximum number of archived tasks to return")
|
|
557
|
+
}),
|
|
558
|
+
handler: async (args, taskOps2) => {
|
|
559
|
+
const tasks = await taskOps2.getArchivedTasks(args.limit);
|
|
560
|
+
return {
|
|
561
|
+
content: [
|
|
562
|
+
{
|
|
563
|
+
type: "text",
|
|
564
|
+
text: JSON.stringify(
|
|
565
|
+
{
|
|
566
|
+
success: true,
|
|
567
|
+
archived_tasks: tasks.map((t) => ({
|
|
568
|
+
id: t.id,
|
|
569
|
+
title: t.title,
|
|
570
|
+
completed_at: t.completed_at,
|
|
571
|
+
archived_at: t.archived_at,
|
|
572
|
+
priority: t.priority,
|
|
573
|
+
tags: t.tags?.map((tag) => tag.name) || []
|
|
574
|
+
})),
|
|
575
|
+
count: tasks.length
|
|
576
|
+
},
|
|
577
|
+
null,
|
|
578
|
+
2
|
|
579
|
+
)
|
|
580
|
+
}
|
|
581
|
+
]
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
// unarchive_task
|
|
586
|
+
{
|
|
587
|
+
name: "unarchive_task",
|
|
588
|
+
description: "Restore an archived task. Moves the task from archived status back to done (or optionally another status).",
|
|
589
|
+
inputSchema: z.object({
|
|
590
|
+
task_id: z.string().describe("Task ID to unarchive"),
|
|
591
|
+
restore_status: z.enum(["todo", "vibing", "done"]).default("done").describe("Status to restore the task to (default: done)")
|
|
592
|
+
}),
|
|
593
|
+
handler: async (args, taskOps2) => {
|
|
594
|
+
const task = await taskOps2.unarchiveTask(args.task_id, args.restore_status);
|
|
595
|
+
return {
|
|
596
|
+
content: [
|
|
597
|
+
{
|
|
598
|
+
type: "text",
|
|
599
|
+
text: JSON.stringify(
|
|
600
|
+
{
|
|
601
|
+
success: true,
|
|
602
|
+
task: {
|
|
603
|
+
id: task.id,
|
|
604
|
+
title: task.title,
|
|
605
|
+
status: task.status
|
|
606
|
+
},
|
|
607
|
+
message: `Task restored to '${args.restore_status}' status.`
|
|
608
|
+
},
|
|
609
|
+
null,
|
|
610
|
+
2
|
|
611
|
+
)
|
|
612
|
+
}
|
|
613
|
+
]
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
// capture_error
|
|
618
|
+
{
|
|
619
|
+
name: "capture_error",
|
|
620
|
+
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.",
|
|
621
|
+
inputSchema: z.object({
|
|
622
|
+
error_text: z.string().describe("The raw error text to parse"),
|
|
623
|
+
source: z.enum(["terminal", "browser", "expo", "other"]).default("other").describe("Source of the error (helps with parsing)")
|
|
624
|
+
}),
|
|
625
|
+
handler: async (args, _taskOps) => {
|
|
626
|
+
const { error_text, source } = args;
|
|
627
|
+
const parsed = parseError(error_text, source);
|
|
628
|
+
return {
|
|
629
|
+
content: [
|
|
630
|
+
{
|
|
631
|
+
type: "text",
|
|
632
|
+
text: JSON.stringify(
|
|
633
|
+
{
|
|
634
|
+
success: true,
|
|
635
|
+
...parsed
|
|
636
|
+
},
|
|
637
|
+
null,
|
|
638
|
+
2
|
|
639
|
+
)
|
|
640
|
+
}
|
|
641
|
+
]
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
},
|
|
645
|
+
// summarize_errors
|
|
646
|
+
{
|
|
647
|
+
name: "summarize_errors",
|
|
648
|
+
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).",
|
|
649
|
+
inputSchema: z.object({
|
|
650
|
+
log_text: z.string().describe("The raw log output containing errors"),
|
|
651
|
+
max_lines: z.number().default(100).describe("Maximum lines in the summary output (default: 100)")
|
|
652
|
+
}),
|
|
653
|
+
handler: async (args, _taskOps) => {
|
|
654
|
+
const { log_text, max_lines = 100 } = args;
|
|
655
|
+
const result = summarizeErrors(log_text, max_lines);
|
|
656
|
+
return {
|
|
657
|
+
content: [
|
|
658
|
+
{
|
|
659
|
+
type: "text",
|
|
660
|
+
text: JSON.stringify(
|
|
661
|
+
{
|
|
662
|
+
success: true,
|
|
663
|
+
...result
|
|
664
|
+
},
|
|
665
|
+
null,
|
|
666
|
+
2
|
|
667
|
+
)
|
|
668
|
+
}
|
|
669
|
+
]
|
|
670
|
+
};
|
|
671
|
+
}
|
|
353
672
|
}
|
|
354
673
|
];
|
|
355
674
|
}
|
|
675
|
+
function parseError(errorText, source) {
|
|
676
|
+
const lines = errorText.split("\n");
|
|
677
|
+
let errorType = "Unknown";
|
|
678
|
+
let file = null;
|
|
679
|
+
let line = null;
|
|
680
|
+
let column = null;
|
|
681
|
+
let message = "";
|
|
682
|
+
const stackFrames = [];
|
|
683
|
+
const patterns = {
|
|
684
|
+
// Node.js/JavaScript: "TypeError: Cannot read property 'x' of undefined"
|
|
685
|
+
jsError: /^(\w+Error):\s*(.+)$/,
|
|
686
|
+
// Python: "TypeError: unsupported operand type(s)"
|
|
687
|
+
pythonError: /^(\w+Error|Exception):\s*(.+)$/,
|
|
688
|
+
// Stack frame patterns
|
|
689
|
+
// Node.js: " at functionName (file.js:10:5)"
|
|
690
|
+
nodeStackFrame: /^\s*at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?$/,
|
|
691
|
+
// Python: ' File "script.py", line 10, in function_name'
|
|
692
|
+
pythonStackFrame: /^\s*File\s+"([^"]+)",\s*line\s*(\d+)(?:,\s*in\s+(.+))?$/,
|
|
693
|
+
// Webpack/bundler: "ERROR in ./src/file.tsx:10:5"
|
|
694
|
+
webpackError: /^ERROR\s+in\s+\.?(.+?):(\d+):(\d+)$/,
|
|
695
|
+
// TypeScript: "src/file.ts(10,5): error TS2345:"
|
|
696
|
+
tsError: /^(.+?)\((\d+),(\d+)\):\s*(error\s+TS\d+):\s*(.+)$/,
|
|
697
|
+
// Expo/React Native: "Error: ..." with component stack
|
|
698
|
+
expoError: /^Error:\s*(.+)$/,
|
|
699
|
+
// Generic file:line:col pattern
|
|
700
|
+
genericFileLine: /([^\s:]+\.[a-zA-Z]+):(\d+)(?::(\d+))?/,
|
|
701
|
+
// ESLint/Prettier style: " 10:5 error Message"
|
|
702
|
+
lintError: /^\s*(\d+):(\d+)\s+(error|warning)\s+(.+)$/
|
|
703
|
+
};
|
|
704
|
+
for (let i = 0; i < lines.length; i++) {
|
|
705
|
+
const trimmedLine = lines[i].trim();
|
|
706
|
+
if (!trimmedLine) continue;
|
|
707
|
+
const tsMatch = trimmedLine.match(patterns.tsError);
|
|
708
|
+
if (tsMatch) {
|
|
709
|
+
file = tsMatch[1];
|
|
710
|
+
line = parseInt(tsMatch[2], 10);
|
|
711
|
+
column = parseInt(tsMatch[3], 10);
|
|
712
|
+
errorType = tsMatch[4];
|
|
713
|
+
message = tsMatch[5];
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
const webpackMatch = trimmedLine.match(patterns.webpackError);
|
|
717
|
+
if (webpackMatch) {
|
|
718
|
+
file = webpackMatch[1];
|
|
719
|
+
line = parseInt(webpackMatch[2], 10);
|
|
720
|
+
column = parseInt(webpackMatch[3], 10);
|
|
721
|
+
errorType = "WebpackError";
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
const jsMatch = trimmedLine.match(patterns.jsError);
|
|
725
|
+
if (jsMatch && !message) {
|
|
726
|
+
errorType = jsMatch[1];
|
|
727
|
+
message = jsMatch[2];
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
const pythonMatch = trimmedLine.match(patterns.pythonError);
|
|
731
|
+
if (pythonMatch && !message) {
|
|
732
|
+
errorType = pythonMatch[1];
|
|
733
|
+
message = pythonMatch[2];
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (source === "expo") {
|
|
737
|
+
const expoMatch = trimmedLine.match(patterns.expoError);
|
|
738
|
+
if (expoMatch && !message) {
|
|
739
|
+
errorType = "ExpoError";
|
|
740
|
+
message = expoMatch[1];
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const nodeFrameMatch = trimmedLine.match(patterns.nodeStackFrame);
|
|
745
|
+
if (nodeFrameMatch) {
|
|
746
|
+
const framePath = nodeFrameMatch[2];
|
|
747
|
+
const frameLine = nodeFrameMatch[3];
|
|
748
|
+
const frameCol = nodeFrameMatch[4];
|
|
749
|
+
const funcName = nodeFrameMatch[1] || "<anonymous>";
|
|
750
|
+
if (!isLibraryFrame(framePath)) {
|
|
751
|
+
stackFrames.push(`${funcName} at ${framePath}:${frameLine}:${frameCol}`);
|
|
752
|
+
if (!file) {
|
|
753
|
+
file = framePath;
|
|
754
|
+
line = parseInt(frameLine, 10);
|
|
755
|
+
column = parseInt(frameCol, 10);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
const pythonFrameMatch = trimmedLine.match(patterns.pythonStackFrame);
|
|
761
|
+
if (pythonFrameMatch) {
|
|
762
|
+
const framePath = pythonFrameMatch[1];
|
|
763
|
+
const frameLine = pythonFrameMatch[2];
|
|
764
|
+
const funcName = pythonFrameMatch[3] || "<module>";
|
|
765
|
+
if (!isLibraryFrame(framePath)) {
|
|
766
|
+
stackFrames.push(`${funcName} at ${framePath}:${frameLine}`);
|
|
767
|
+
file = framePath;
|
|
768
|
+
line = parseInt(frameLine, 10);
|
|
769
|
+
}
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
const lintMatch = trimmedLine.match(patterns.lintError);
|
|
773
|
+
if (lintMatch) {
|
|
774
|
+
line = parseInt(lintMatch[1], 10);
|
|
775
|
+
column = parseInt(lintMatch[2], 10);
|
|
776
|
+
errorType = lintMatch[3] === "error" ? "LintError" : "LintWarning";
|
|
777
|
+
message = lintMatch[4];
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
if (!file) {
|
|
781
|
+
const genericMatch = trimmedLine.match(patterns.genericFileLine);
|
|
782
|
+
if (genericMatch && !isLibraryFrame(genericMatch[1])) {
|
|
783
|
+
file = genericMatch[1];
|
|
784
|
+
line = parseInt(genericMatch[2], 10);
|
|
785
|
+
column = genericMatch[3] ? parseInt(genericMatch[3], 10) : null;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
if (errorType !== "Unknown" && !message && trimmedLine && !trimmedLine.startsWith("at ")) {
|
|
789
|
+
message = trimmedLine;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (!message) {
|
|
793
|
+
message = lines.find((l) => l.trim())?.trim() || "No error message found";
|
|
794
|
+
}
|
|
795
|
+
const rawExcerpt = lines.slice(0, 10).join("\n");
|
|
796
|
+
return {
|
|
797
|
+
error_type: errorType,
|
|
798
|
+
file,
|
|
799
|
+
line,
|
|
800
|
+
column,
|
|
801
|
+
message,
|
|
802
|
+
stack_summary: stackFrames.slice(0, 5),
|
|
803
|
+
// Limit to 5 frames
|
|
804
|
+
raw_excerpt: rawExcerpt
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
function isLibraryFrame(filePath) {
|
|
808
|
+
const libraryPatterns = [
|
|
809
|
+
/node_modules/,
|
|
810
|
+
/site-packages/,
|
|
811
|
+
/dist-packages/,
|
|
812
|
+
/\.pnpm/,
|
|
813
|
+
/\.yarn/,
|
|
814
|
+
/internal\//,
|
|
815
|
+
/^node:/,
|
|
816
|
+
/<anonymous>/,
|
|
817
|
+
/webpack-internal/,
|
|
818
|
+
/react-dom/,
|
|
819
|
+
/react-native/,
|
|
820
|
+
/expo/,
|
|
821
|
+
/metro/,
|
|
822
|
+
/hermes/
|
|
823
|
+
];
|
|
824
|
+
return libraryPatterns.some((pattern) => pattern.test(filePath));
|
|
825
|
+
}
|
|
826
|
+
function summarizeErrors(logText, maxLines = 100) {
|
|
827
|
+
const errorTypePatterns = [
|
|
828
|
+
{ type: "TypeError", pattern: /TypeError:\s*(.+)/i },
|
|
829
|
+
{ type: "SyntaxError", pattern: /SyntaxError:\s*(.+)/i },
|
|
830
|
+
{ type: "ReferenceError", pattern: /ReferenceError:\s*(.+)/i },
|
|
831
|
+
{ type: "RangeError", pattern: /RangeError:\s*(.+)/i },
|
|
832
|
+
{ type: "EvalError", pattern: /EvalError:\s*(.+)/i },
|
|
833
|
+
{ type: "URIError", pattern: /URIError:\s*(.+)/i },
|
|
834
|
+
{ type: "AggregateError", pattern: /AggregateError:\s*(.+)/i },
|
|
835
|
+
{ type: "InternalError", pattern: /InternalError:\s*(.+)/i },
|
|
836
|
+
{ type: "AssertionError", pattern: /AssertionError:\s*(.+)/i },
|
|
837
|
+
{ type: "ValidationError", pattern: /ValidationError:\s*(.+)/i },
|
|
838
|
+
{ type: "NetworkError", pattern: /NetworkError:\s*(.+)/i },
|
|
839
|
+
{ type: "TimeoutError", pattern: /TimeoutError:\s*(.+)/i },
|
|
840
|
+
{ type: "ENOENT", pattern: /ENOENT:\s*(.+)/i },
|
|
841
|
+
{ type: "EACCES", pattern: /EACCES:\s*(.+)/i },
|
|
842
|
+
{ type: "ECONNREFUSED", pattern: /ECONNREFUSED:\s*(.+)/i },
|
|
843
|
+
{ type: "ETIMEDOUT", pattern: /ETIMEDOUT:\s*(.+)/i },
|
|
844
|
+
{ type: "ModuleNotFoundError", pattern: /ModuleNotFoundError:\s*(.+)/i },
|
|
845
|
+
{ type: "ImportError", pattern: /ImportError:\s*(.+)/i },
|
|
846
|
+
{ type: "AttributeError", pattern: /AttributeError:\s*(.+)/i },
|
|
847
|
+
{ type: "KeyError", pattern: /KeyError:\s*(.+)/i },
|
|
848
|
+
{ type: "ValueError", pattern: /ValueError:\s*(.+)/i },
|
|
849
|
+
{ type: "IndexError", pattern: /IndexError:\s*(.+)/i },
|
|
850
|
+
{ type: "Error", pattern: /(?:^|\s)Error:\s*(.+)/i },
|
|
851
|
+
{ type: "Warning", pattern: /(?:^|\s)(?:Warning|WARN):\s*(.+)/i },
|
|
852
|
+
{ type: "Failed", pattern: /(?:FAILED|Failed|failed):\s*(.+)/i },
|
|
853
|
+
{ type: "Exception", pattern: /Exception:\s*(.+)/i }
|
|
854
|
+
];
|
|
855
|
+
const lines = logText.split("\n");
|
|
856
|
+
const totalLines = lines.length;
|
|
857
|
+
const errorMap = /* @__PURE__ */ new Map();
|
|
858
|
+
let totalErrorCount = 0;
|
|
859
|
+
const filePathPattern = /(?:at\s+(?:.*?\s+\()?)?([A-Za-z]:)?([/\\](?!node_modules)[^\s:()]+\.[a-z]+)(?::(\d+))?(?::(\d+))?/gi;
|
|
860
|
+
const nodeModulesPattern = /node_modules/i;
|
|
861
|
+
function normalizeMessage(msg) {
|
|
862
|
+
return msg.replace(/['"`].*?['"`]/g, "<string>").replace(/\b\d+\b/g, "<num>").replace(/0x[0-9a-fA-F]+/g, "<hex>").replace(/\{[^}]*\}/g, "<obj>").replace(/\[[^\]]*\]/g, "<arr>").replace(
|
|
863
|
+
/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,
|
|
864
|
+
"<uuid>"
|
|
865
|
+
).replace(/\s+/g, " ").trim().substring(0, 200);
|
|
866
|
+
}
|
|
867
|
+
function extractAppFiles(line) {
|
|
868
|
+
const files = [];
|
|
869
|
+
const matches = line.matchAll(filePathPattern);
|
|
870
|
+
for (const match of matches) {
|
|
871
|
+
const fullPath = (match[1] || "") + match[2];
|
|
872
|
+
if (!nodeModulesPattern.test(fullPath)) {
|
|
873
|
+
const lineNum = match[3] ? `:${match[3]}` : "";
|
|
874
|
+
files.push(fullPath + lineNum);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return files;
|
|
878
|
+
}
|
|
879
|
+
for (let i = 0; i < lines.length; i++) {
|
|
880
|
+
const line = lines[i];
|
|
881
|
+
if (!line.trim()) continue;
|
|
882
|
+
for (const { type, pattern } of errorTypePatterns) {
|
|
883
|
+
const match = line.match(pattern);
|
|
884
|
+
if (match) {
|
|
885
|
+
totalErrorCount++;
|
|
886
|
+
const message = match[1] || line;
|
|
887
|
+
const normalized = normalizeMessage(message);
|
|
888
|
+
const key = `${type}:${normalized}`;
|
|
889
|
+
const contextLines = lines.slice(i, Math.min(i + 6, lines.length));
|
|
890
|
+
const files = /* @__PURE__ */ new Set();
|
|
891
|
+
for (const contextLine of contextLines) {
|
|
892
|
+
extractAppFiles(contextLine).forEach((f) => files.add(f));
|
|
893
|
+
}
|
|
894
|
+
if (errorMap.has(key)) {
|
|
895
|
+
const existing = errorMap.get(key);
|
|
896
|
+
existing.count++;
|
|
897
|
+
files.forEach((f) => existing.files.add(f));
|
|
898
|
+
} else {
|
|
899
|
+
let example = line;
|
|
900
|
+
for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) {
|
|
901
|
+
const stackLine = lines[j];
|
|
902
|
+
if (stackLine.trim().startsWith("at ") && !nodeModulesPattern.test(stackLine)) {
|
|
903
|
+
example += "\n" + stackLine;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
errorMap.set(key, {
|
|
907
|
+
type,
|
|
908
|
+
message: message.substring(0, 150),
|
|
909
|
+
normalizedMessage: normalized,
|
|
910
|
+
files,
|
|
911
|
+
count: 1,
|
|
912
|
+
firstOccurrence: i + 1,
|
|
913
|
+
example: example.substring(0, 500)
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
const groupedErrors = Array.from(errorMap.values()).sort((a, b) => b.count - a.count).map((e) => ({
|
|
921
|
+
type: e.type,
|
|
922
|
+
count: e.count,
|
|
923
|
+
example: e.example,
|
|
924
|
+
files: Array.from(e.files).slice(0, 5)
|
|
925
|
+
// Limit files per error
|
|
926
|
+
}));
|
|
927
|
+
const summaryLines = [];
|
|
928
|
+
summaryLines.push(`=== ERROR SUMMARY ===`);
|
|
929
|
+
summaryLines.push(`Total lines processed: ${totalLines}`);
|
|
930
|
+
summaryLines.push(`Total errors found: ${totalErrorCount}`);
|
|
931
|
+
summaryLines.push(`Unique error patterns: ${groupedErrors.length}`);
|
|
932
|
+
summaryLines.push("");
|
|
933
|
+
const typeCount = /* @__PURE__ */ new Map();
|
|
934
|
+
for (const err of groupedErrors) {
|
|
935
|
+
typeCount.set(err.type, (typeCount.get(err.type) || 0) + err.count);
|
|
936
|
+
}
|
|
937
|
+
summaryLines.push("--- Errors by Type ---");
|
|
938
|
+
for (const [type, count] of Array.from(typeCount.entries()).sort(
|
|
939
|
+
(a, b) => b[1] - a[1]
|
|
940
|
+
)) {
|
|
941
|
+
summaryLines.push(` ${type}: ${count}`);
|
|
942
|
+
}
|
|
943
|
+
summaryLines.push("");
|
|
944
|
+
summaryLines.push("--- Top Errors (by frequency) ---");
|
|
945
|
+
let currentLines = summaryLines.length;
|
|
946
|
+
const maxDetailedErrors = Math.min(groupedErrors.length, 20);
|
|
947
|
+
for (let i = 0; i < maxDetailedErrors && currentLines < maxLines - 5; i++) {
|
|
948
|
+
const err = groupedErrors[i];
|
|
949
|
+
summaryLines.push("");
|
|
950
|
+
summaryLines.push(`[${i + 1}] ${err.type} (x${err.count})`);
|
|
951
|
+
const exampleLines = err.example.split("\n");
|
|
952
|
+
for (const exLine of exampleLines) {
|
|
953
|
+
if (currentLines >= maxLines - 3) break;
|
|
954
|
+
summaryLines.push(` ${exLine}`);
|
|
955
|
+
currentLines++;
|
|
956
|
+
}
|
|
957
|
+
if (err.files.length > 0) {
|
|
958
|
+
summaryLines.push(` Files: ${err.files.join(", ")}`);
|
|
959
|
+
}
|
|
960
|
+
currentLines = summaryLines.length;
|
|
961
|
+
}
|
|
962
|
+
const summary = summaryLines.slice(0, maxLines).join("\n");
|
|
963
|
+
return {
|
|
964
|
+
summary,
|
|
965
|
+
error_count: totalErrorCount,
|
|
966
|
+
unique_errors: groupedErrors.length,
|
|
967
|
+
grouped_errors: groupedErrors.slice(0, 50)
|
|
968
|
+
// Limit to top 50
|
|
969
|
+
};
|
|
970
|
+
}
|
|
356
971
|
|
|
357
972
|
// src/resources/index.ts
|
|
358
973
|
function setupResources(taskOps) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibetasks/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
48
|
-
"@vibetasks/core": "^0.
|
|
48
|
+
"@vibetasks/core": "^0.3.0",
|
|
49
49
|
"zod": "^3.22.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|