@vibegrid/mcp 0.3.2 → 0.4.0-beta.1
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 +157 -538
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -300,6 +300,23 @@ function createSchema() {
|
|
|
300
300
|
CREATE INDEX IF NOT EXISTS idx_workflow_runs_task ON workflow_runs(trigger_task_id);
|
|
301
301
|
CREATE INDEX IF NOT EXISTS idx_workflow_run_nodes_run ON workflow_run_nodes(run_id);
|
|
302
302
|
CREATE INDEX IF NOT EXISTS idx_workflow_run_nodes_task ON workflow_run_nodes(task_id);
|
|
303
|
+
|
|
304
|
+
CREATE TABLE IF NOT EXISTS session_logs (
|
|
305
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
306
|
+
task_id TEXT NOT NULL,
|
|
307
|
+
session_id TEXT NOT NULL,
|
|
308
|
+
agent_type TEXT,
|
|
309
|
+
branch TEXT,
|
|
310
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
311
|
+
started_at TEXT NOT NULL,
|
|
312
|
+
completed_at TEXT,
|
|
313
|
+
exit_code INTEGER,
|
|
314
|
+
logs TEXT,
|
|
315
|
+
project_name TEXT
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
CREATE INDEX IF NOT EXISTS idx_session_logs_task ON session_logs(task_id);
|
|
319
|
+
CREATE INDEX IF NOT EXISTS idx_session_logs_session ON session_logs(session_id);
|
|
303
320
|
`);
|
|
304
321
|
migrateSchema(d);
|
|
305
322
|
}
|
|
@@ -1525,59 +1542,37 @@ var AGENT_TYPES3 = [
|
|
|
1525
1542
|
function registerSessionTools(server) {
|
|
1526
1543
|
server.tool(
|
|
1527
1544
|
"list_sessions",
|
|
1528
|
-
|
|
1545
|
+
'List terminal sessions. Filter by status: "active" (running terminals), "recent" (past sessions), or "archived".',
|
|
1529
1546
|
{
|
|
1530
|
-
|
|
1547
|
+
filter: z4.enum(["active", "recent", "archived"]).optional().describe("Session filter (default: active)"),
|
|
1548
|
+
project_name: V.name.optional().describe("Filter by project name"),
|
|
1549
|
+
project_path: V.absolutePath.optional().describe("Filter by project path (for recent sessions)")
|
|
1531
1550
|
},
|
|
1532
1551
|
async (args) => {
|
|
1552
|
+
const filter = args.filter ?? "active";
|
|
1533
1553
|
try {
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1554
|
+
if (filter === "active") {
|
|
1555
|
+
let sessions = await rpcCall("terminal:listActive");
|
|
1556
|
+
if (args.project_name) {
|
|
1557
|
+
sessions = sessions.filter((s) => s.projectName === args.project_name);
|
|
1558
|
+
}
|
|
1559
|
+
const summary = sessions.map((s) => ({
|
|
1560
|
+
id: s.id,
|
|
1561
|
+
agentType: s.agentType,
|
|
1562
|
+
projectName: s.projectName,
|
|
1563
|
+
status: s.status,
|
|
1564
|
+
displayName: s.displayName,
|
|
1565
|
+
branch: s.branch,
|
|
1566
|
+
pid: s.pid
|
|
1567
|
+
}));
|
|
1568
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
1569
|
+
} else if (filter === "recent") {
|
|
1570
|
+
const sessions = await rpcCall("sessions:getRecent", args.project_path);
|
|
1571
|
+
return { content: [{ type: "text", text: JSON.stringify(sessions, null, 2) }] };
|
|
1572
|
+
} else {
|
|
1573
|
+
const sessions = await rpcCall("session:listArchived");
|
|
1574
|
+
return { content: [{ type: "text", text: JSON.stringify(sessions, null, 2) }] };
|
|
1537
1575
|
}
|
|
1538
|
-
const summary = sessions.map((s) => ({
|
|
1539
|
-
id: s.id,
|
|
1540
|
-
agentType: s.agentType,
|
|
1541
|
-
projectName: s.projectName,
|
|
1542
|
-
status: s.status,
|
|
1543
|
-
displayName: s.displayName,
|
|
1544
|
-
branch: s.branch,
|
|
1545
|
-
pid: s.pid
|
|
1546
|
-
}));
|
|
1547
|
-
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
1548
|
-
} catch (err) {
|
|
1549
|
-
return {
|
|
1550
|
-
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
|
|
1551
|
-
isError: true
|
|
1552
|
-
};
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
);
|
|
1556
|
-
server.tool(
|
|
1557
|
-
"list_recent_sessions",
|
|
1558
|
-
"List recent session history for a project. Requires the VibeGrid app to be running.",
|
|
1559
|
-
{
|
|
1560
|
-
project_path: V.absolutePath.optional().describe("Filter by project path")
|
|
1561
|
-
},
|
|
1562
|
-
async (args) => {
|
|
1563
|
-
try {
|
|
1564
|
-
const sessions = await rpcCall("sessions:getRecent", args.project_path);
|
|
1565
|
-
return { content: [{ type: "text", text: JSON.stringify(sessions, null, 2) }] };
|
|
1566
|
-
} catch (err) {
|
|
1567
|
-
return {
|
|
1568
|
-
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
|
|
1569
|
-
isError: true
|
|
1570
|
-
};
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
);
|
|
1574
|
-
server.tool(
|
|
1575
|
-
"list_archived_sessions",
|
|
1576
|
-
"List archived sessions. Requires the VibeGrid app to be running.",
|
|
1577
|
-
async () => {
|
|
1578
|
-
try {
|
|
1579
|
-
const sessions = await rpcCall("session:listArchived");
|
|
1580
|
-
return { content: [{ type: "text", text: JSON.stringify(sessions, null, 2) }] };
|
|
1581
1576
|
} catch (err) {
|
|
1582
1577
|
return {
|
|
1583
1578
|
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
|
|
@@ -1587,8 +1582,8 @@ function registerSessionTools(server) {
|
|
|
1587
1582
|
}
|
|
1588
1583
|
);
|
|
1589
1584
|
server.tool(
|
|
1590
|
-
"
|
|
1591
|
-
"Launch an AI agent
|
|
1585
|
+
"launch_session",
|
|
1586
|
+
"Launch an AI agent session (interactive terminal or headless). Requires the VibeGrid app to be running.",
|
|
1592
1587
|
{
|
|
1593
1588
|
agent_type: z4.enum(AGENT_TYPES3).describe("Agent type to launch"),
|
|
1594
1589
|
project_name: V.name.describe("Project name"),
|
|
@@ -1596,7 +1591,8 @@ function registerSessionTools(server) {
|
|
|
1596
1591
|
prompt: V.prompt.optional().describe("Initial prompt to send to the agent"),
|
|
1597
1592
|
branch: V.shortText.optional().describe("Git branch to checkout"),
|
|
1598
1593
|
use_worktree: z4.boolean().optional().describe("Create a git worktree"),
|
|
1599
|
-
display_name: V.shortText.optional().describe("Display name for the session")
|
|
1594
|
+
display_name: V.shortText.optional().describe("Display name for the session"),
|
|
1595
|
+
headless: z4.boolean().optional().describe("Launch as headless (no UI) session")
|
|
1600
1596
|
},
|
|
1601
1597
|
async (args) => {
|
|
1602
1598
|
const payload = {
|
|
@@ -1608,8 +1604,10 @@ function registerSessionTools(server) {
|
|
|
1608
1604
|
...args.use_worktree && { useWorktree: args.use_worktree },
|
|
1609
1605
|
...args.display_name && { displayName: args.display_name }
|
|
1610
1606
|
};
|
|
1607
|
+
const rpcMethod = args.headless ? "headless:create" : "terminal:create";
|
|
1608
|
+
const label = args.headless ? "headless" : "terminal";
|
|
1611
1609
|
try {
|
|
1612
|
-
const session = await rpcCall(
|
|
1610
|
+
const session = await rpcCall(rpcMethod, payload);
|
|
1613
1611
|
return {
|
|
1614
1612
|
content: [
|
|
1615
1613
|
{
|
|
@@ -1633,7 +1631,7 @@ function registerSessionTools(server) {
|
|
|
1633
1631
|
content: [
|
|
1634
1632
|
{
|
|
1635
1633
|
type: "text",
|
|
1636
|
-
text: `Error launching agent: ${err instanceof Error ? err.message : err}`
|
|
1634
|
+
text: `Error launching ${label} agent: ${err instanceof Error ? err.message : err}`
|
|
1637
1635
|
}
|
|
1638
1636
|
],
|
|
1639
1637
|
isError: true
|
|
@@ -1642,53 +1640,50 @@ function registerSessionTools(server) {
|
|
|
1642
1640
|
}
|
|
1643
1641
|
);
|
|
1644
1642
|
server.tool(
|
|
1645
|
-
"
|
|
1646
|
-
"
|
|
1643
|
+
"kill_session",
|
|
1644
|
+
"Kill a terminal or headless session. Requires the VibeGrid app to be running.",
|
|
1647
1645
|
{
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
project_path: V.absolutePath.describe("Absolute path to project directory"),
|
|
1651
|
-
prompt: V.prompt.optional().describe("Initial prompt to send to the agent"),
|
|
1652
|
-
branch: V.shortText.optional().describe("Git branch to checkout"),
|
|
1653
|
-
use_worktree: z4.boolean().optional().describe("Create a git worktree"),
|
|
1654
|
-
display_name: V.shortText.optional().describe("Display name for the session")
|
|
1646
|
+
id: V.id.describe("Session ID to kill"),
|
|
1647
|
+
headless: z4.boolean().optional().describe("Kill a headless session instead of a terminal")
|
|
1655
1648
|
},
|
|
1656
1649
|
async (args) => {
|
|
1657
|
-
const
|
|
1658
|
-
|
|
1659
|
-
projectName: args.project_name,
|
|
1660
|
-
projectPath: args.project_path,
|
|
1661
|
-
...args.prompt && { initialPrompt: args.prompt },
|
|
1662
|
-
...args.branch && { branch: args.branch },
|
|
1663
|
-
...args.use_worktree && { useWorktree: args.use_worktree },
|
|
1664
|
-
...args.display_name && { displayName: args.display_name }
|
|
1665
|
-
};
|
|
1650
|
+
const rpcMethod = args.headless ? "headless:kill" : "terminal:kill";
|
|
1651
|
+
const label = args.headless ? "headless session" : "session";
|
|
1666
1652
|
try {
|
|
1667
|
-
|
|
1653
|
+
await rpcCall(rpcMethod, args.id);
|
|
1654
|
+
return { content: [{ type: "text", text: `Killed ${label}: ${args.id}` }] };
|
|
1655
|
+
} catch (err) {
|
|
1668
1656
|
return {
|
|
1669
1657
|
content: [
|
|
1670
1658
|
{
|
|
1671
1659
|
type: "text",
|
|
1672
|
-
text:
|
|
1673
|
-
{
|
|
1674
|
-
id: session.id,
|
|
1675
|
-
agentType: session.agentType,
|
|
1676
|
-
projectName: session.projectName,
|
|
1677
|
-
pid: session.pid,
|
|
1678
|
-
status: session.status
|
|
1679
|
-
},
|
|
1680
|
-
null,
|
|
1681
|
-
2
|
|
1682
|
-
)
|
|
1660
|
+
text: `Error killing ${label}: ${err instanceof Error ? err.message : err}`
|
|
1683
1661
|
}
|
|
1684
|
-
]
|
|
1662
|
+
],
|
|
1663
|
+
isError: true
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
);
|
|
1668
|
+
server.tool(
|
|
1669
|
+
"rename_session",
|
|
1670
|
+
"Rename a terminal session. Changes the display name shown in the UI.",
|
|
1671
|
+
{
|
|
1672
|
+
id: V.id.describe("Session ID"),
|
|
1673
|
+
display_name: V.shortText.describe("New display name")
|
|
1674
|
+
},
|
|
1675
|
+
async (args) => {
|
|
1676
|
+
try {
|
|
1677
|
+
await rpcCall("terminal:rename", { id: args.id, displayName: args.display_name });
|
|
1678
|
+
return {
|
|
1679
|
+
content: [{ type: "text", text: `Renamed session ${args.id} to "${args.display_name}"` }]
|
|
1685
1680
|
};
|
|
1686
1681
|
} catch (err) {
|
|
1687
1682
|
return {
|
|
1688
1683
|
content: [
|
|
1689
1684
|
{
|
|
1690
1685
|
type: "text",
|
|
1691
|
-
text: `Error
|
|
1686
|
+
text: `Error renaming session: ${err instanceof Error ? err.message : err}`
|
|
1692
1687
|
}
|
|
1693
1688
|
],
|
|
1694
1689
|
isError: true
|
|
@@ -1697,19 +1692,28 @@ function registerSessionTools(server) {
|
|
|
1697
1692
|
}
|
|
1698
1693
|
);
|
|
1699
1694
|
server.tool(
|
|
1700
|
-
"
|
|
1701
|
-
"
|
|
1702
|
-
{
|
|
1695
|
+
"reorder_sessions",
|
|
1696
|
+
"Reorder terminal sessions in the grid. Provide session IDs in the desired display order.",
|
|
1697
|
+
{
|
|
1698
|
+
session_ids: z4.array(V.id).min(1, "At least one session ID is required").describe("Session IDs in desired order")
|
|
1699
|
+
},
|
|
1703
1700
|
async (args) => {
|
|
1704
1701
|
try {
|
|
1705
|
-
await rpcCall("terminal:
|
|
1706
|
-
return {
|
|
1702
|
+
await rpcCall("terminal:reorder", args.session_ids);
|
|
1703
|
+
return {
|
|
1704
|
+
content: [
|
|
1705
|
+
{
|
|
1706
|
+
type: "text",
|
|
1707
|
+
text: `Reordered ${args.session_ids.length} sessions`
|
|
1708
|
+
}
|
|
1709
|
+
]
|
|
1710
|
+
};
|
|
1707
1711
|
} catch (err) {
|
|
1708
1712
|
return {
|
|
1709
1713
|
content: [
|
|
1710
1714
|
{
|
|
1711
1715
|
type: "text",
|
|
1712
|
-
text: `Error
|
|
1716
|
+
text: `Error reordering sessions: ${err instanceof Error ? err.message : err}`
|
|
1713
1717
|
}
|
|
1714
1718
|
],
|
|
1715
1719
|
isError: true
|
|
@@ -1718,19 +1722,27 @@ function registerSessionTools(server) {
|
|
|
1718
1722
|
}
|
|
1719
1723
|
);
|
|
1720
1724
|
server.tool(
|
|
1721
|
-
"
|
|
1722
|
-
"
|
|
1723
|
-
{
|
|
1725
|
+
"read_session_output",
|
|
1726
|
+
"Read terminal output from a running session. Output is stored in a rolling 1000-line buffer with ANSI codes stripped.",
|
|
1727
|
+
{
|
|
1728
|
+
id: V.id.describe("Session ID"),
|
|
1729
|
+
lines: z4.number().int().min(1).max(1e3).optional().describe("Number of lines to read from the end (default: all)")
|
|
1730
|
+
},
|
|
1724
1731
|
async (args) => {
|
|
1725
1732
|
try {
|
|
1726
|
-
await rpcCall("
|
|
1727
|
-
|
|
1733
|
+
const output = await rpcCall("terminal:readOutput", {
|
|
1734
|
+
id: args.id,
|
|
1735
|
+
lines: args.lines
|
|
1736
|
+
});
|
|
1737
|
+
return {
|
|
1738
|
+
content: [{ type: "text", text: output.join("\n") }]
|
|
1739
|
+
};
|
|
1728
1740
|
} catch (err) {
|
|
1729
1741
|
return {
|
|
1730
1742
|
content: [
|
|
1731
1743
|
{
|
|
1732
1744
|
type: "text",
|
|
1733
|
-
text: `Error
|
|
1745
|
+
text: `Error reading session output: ${err instanceof Error ? err.message : err}`
|
|
1734
1746
|
}
|
|
1735
1747
|
],
|
|
1736
1748
|
isError: true
|
|
@@ -1747,7 +1759,9 @@ function registerSessionTools(server) {
|
|
|
1747
1759
|
},
|
|
1748
1760
|
async (args) => {
|
|
1749
1761
|
try {
|
|
1750
|
-
|
|
1762
|
+
const trimmed = args.data.replace(/[\r\n]+$/, "");
|
|
1763
|
+
const data = trimmed + "\r";
|
|
1764
|
+
await rpcNotify("terminal:write", { id: args.id, data });
|
|
1751
1765
|
return { content: [{ type: "text", text: `Wrote to session: ${args.id}` }] };
|
|
1752
1766
|
} catch (err) {
|
|
1753
1767
|
return {
|
|
@@ -1955,460 +1969,66 @@ function registerWorkflowTools(server) {
|
|
|
1955
1969
|
);
|
|
1956
1970
|
server.tool(
|
|
1957
1971
|
"list_workflow_runs",
|
|
1958
|
-
"List execution history
|
|
1972
|
+
"List workflow execution history. Filter by workflow_id or task_id.",
|
|
1959
1973
|
{
|
|
1960
|
-
workflow_id: V.id.describe("
|
|
1974
|
+
workflow_id: V.id.optional().describe("Filter by workflow ID"),
|
|
1975
|
+
task_id: V.id.optional().describe("Filter by task ID (runs triggered by this task)"),
|
|
1961
1976
|
limit: z5.number().int().min(1).max(100).optional().describe("Max results (default: 20)")
|
|
1962
1977
|
},
|
|
1963
1978
|
async (args) => {
|
|
1964
|
-
|
|
1965
|
-
return { content: [{ type: "text", text: JSON.stringify(runs, null, 2) }] };
|
|
1966
|
-
}
|
|
1967
|
-
);
|
|
1968
|
-
server.tool(
|
|
1969
|
-
"list_workflow_runs_by_task",
|
|
1970
|
-
"List workflow executions triggered by a specific task",
|
|
1971
|
-
{
|
|
1972
|
-
task_id: V.id.describe("Task ID"),
|
|
1973
|
-
limit: z5.number().int().min(1).max(100).optional().describe("Max results (default: 20)")
|
|
1974
|
-
},
|
|
1975
|
-
async (args) => {
|
|
1976
|
-
const runs = listWorkflowRunsByTask(args.task_id, args.limit ?? 20);
|
|
1977
|
-
return { content: [{ type: "text", text: JSON.stringify(runs, null, 2) }] };
|
|
1978
|
-
}
|
|
1979
|
-
);
|
|
1980
|
-
server.tool(
|
|
1981
|
-
"get_scheduler_log",
|
|
1982
|
-
"Get scheduler execution log for a workflow. Requires the VibeGrid app to be running.",
|
|
1983
|
-
{
|
|
1984
|
-
workflow_id: V.id.optional().describe("Workflow ID (omit for all workflows)")
|
|
1985
|
-
},
|
|
1986
|
-
async (args) => {
|
|
1987
|
-
try {
|
|
1988
|
-
const log2 = await rpcCall("scheduler:getLog", args.workflow_id);
|
|
1989
|
-
return { content: [{ type: "text", text: JSON.stringify(log2, null, 2) }] };
|
|
1990
|
-
} catch (err) {
|
|
1991
|
-
return {
|
|
1992
|
-
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
|
|
1993
|
-
isError: true
|
|
1994
|
-
};
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
);
|
|
1998
|
-
server.tool(
|
|
1999
|
-
"get_next_scheduled_run",
|
|
2000
|
-
"Get the next scheduled run time for a workflow. Requires the VibeGrid app to be running.",
|
|
2001
|
-
{
|
|
2002
|
-
workflow_id: V.id.describe("Workflow ID")
|
|
2003
|
-
},
|
|
2004
|
-
async (args) => {
|
|
2005
|
-
try {
|
|
2006
|
-
const nextRun = await rpcCall("scheduler:getNextRun", args.workflow_id);
|
|
1979
|
+
if (args.workflow_id && args.task_id) {
|
|
2007
1980
|
return {
|
|
2008
|
-
content: [
|
|
2009
|
-
{
|
|
2010
|
-
type: "text",
|
|
2011
|
-
text: nextRun ? JSON.stringify({ nextRun }, null, 2) : "No scheduled run (workflow may be manual or disabled)"
|
|
2012
|
-
}
|
|
2013
|
-
]
|
|
2014
|
-
};
|
|
2015
|
-
} catch (err) {
|
|
2016
|
-
return {
|
|
2017
|
-
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
|
|
1981
|
+
content: [{ type: "text", text: "Error: provide workflow_id or task_id, not both" }],
|
|
2018
1982
|
isError: true
|
|
2019
1983
|
};
|
|
2020
1984
|
}
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
// src/tools/git.ts
|
|
2026
|
-
import { z as z6 } from "zod";
|
|
2027
|
-
|
|
2028
|
-
// ../server/src/git-utils.ts
|
|
2029
|
-
import { execFileSync } from "child_process";
|
|
2030
|
-
import path5 from "path";
|
|
2031
|
-
import fs4 from "fs";
|
|
2032
|
-
import crypto3 from "crypto";
|
|
2033
|
-
var EXEC_OPTS = {
|
|
2034
|
-
encoding: "utf-8",
|
|
2035
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
2036
|
-
};
|
|
2037
|
-
function getGitBranch(projectPath) {
|
|
2038
|
-
try {
|
|
2039
|
-
const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
2040
|
-
cwd: projectPath,
|
|
2041
|
-
...EXEC_OPTS,
|
|
2042
|
-
timeout: 3e3
|
|
2043
|
-
}).trim();
|
|
2044
|
-
return branch && branch !== "HEAD" ? branch : null;
|
|
2045
|
-
} catch {
|
|
2046
|
-
return null;
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
|
-
function listBranches(projectPath) {
|
|
2050
|
-
try {
|
|
2051
|
-
const output = execFileSync("git", ["branch", "--format=%(refname:short)"], {
|
|
2052
|
-
cwd: projectPath,
|
|
2053
|
-
...EXEC_OPTS,
|
|
2054
|
-
timeout: 5e3
|
|
2055
|
-
}).trim();
|
|
2056
|
-
return output ? output.split("\n").map((b) => b.trim()).filter(Boolean) : [];
|
|
2057
|
-
} catch {
|
|
2058
|
-
return [];
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
function listRemoteBranches(projectPath) {
|
|
2062
|
-
try {
|
|
2063
|
-
execFileSync("git", ["fetch", "--prune"], {
|
|
2064
|
-
cwd: projectPath,
|
|
2065
|
-
...EXEC_OPTS,
|
|
2066
|
-
timeout: 15e3
|
|
2067
|
-
});
|
|
2068
|
-
const output = execFileSync("git", ["branch", "-r", "--format=%(refname:short)"], {
|
|
2069
|
-
cwd: projectPath,
|
|
2070
|
-
...EXEC_OPTS,
|
|
2071
|
-
timeout: 5e3
|
|
2072
|
-
}).trim();
|
|
2073
|
-
return output ? output.split("\n").map((b) => b.trim().replace(/^origin\//, "")).filter((b) => b && b !== "HEAD") : [];
|
|
2074
|
-
} catch {
|
|
2075
|
-
return [];
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
function createWorktree(projectPath, branch) {
|
|
2079
|
-
const projectName = path5.basename(projectPath);
|
|
2080
|
-
const shortId = crypto3.randomUUID().slice(0, 8);
|
|
2081
|
-
const baseDir = path5.join(path5.dirname(projectPath), ".vibegrid-worktrees", projectName);
|
|
2082
|
-
const worktreeDir = path5.join(baseDir, `${branch}-${shortId}`);
|
|
2083
|
-
fs4.mkdirSync(baseDir, { recursive: true });
|
|
2084
|
-
const localBranches = listBranches(projectPath);
|
|
2085
|
-
if (localBranches.includes(branch)) {
|
|
2086
|
-
try {
|
|
2087
|
-
execFileSync("git", ["worktree", "add", worktreeDir, branch], {
|
|
2088
|
-
cwd: projectPath,
|
|
2089
|
-
...EXEC_OPTS,
|
|
2090
|
-
timeout: 3e4
|
|
2091
|
-
});
|
|
2092
|
-
} catch {
|
|
2093
|
-
const newBranch = `${branch}-worktree-${shortId}`;
|
|
2094
|
-
execFileSync("git", ["worktree", "add", "-b", newBranch, worktreeDir, branch], {
|
|
2095
|
-
cwd: projectPath,
|
|
2096
|
-
...EXEC_OPTS,
|
|
2097
|
-
timeout: 3e4
|
|
2098
|
-
});
|
|
2099
|
-
return { worktreePath: worktreeDir, branch: newBranch };
|
|
2100
|
-
}
|
|
2101
|
-
} else {
|
|
2102
|
-
execFileSync("git", ["worktree", "add", "-b", branch, worktreeDir], {
|
|
2103
|
-
cwd: projectPath,
|
|
2104
|
-
...EXEC_OPTS,
|
|
2105
|
-
timeout: 3e4
|
|
2106
|
-
});
|
|
2107
|
-
}
|
|
2108
|
-
return { worktreePath: worktreeDir, branch };
|
|
2109
|
-
}
|
|
2110
|
-
function isWorktreeDirty(worktreePath) {
|
|
2111
|
-
try {
|
|
2112
|
-
const output = execFileSync("git", ["status", "--porcelain"], {
|
|
2113
|
-
cwd: worktreePath,
|
|
2114
|
-
...EXEC_OPTS,
|
|
2115
|
-
timeout: 5e3
|
|
2116
|
-
}).trim();
|
|
2117
|
-
return output.length > 0;
|
|
2118
|
-
} catch {
|
|
2119
|
-
return true;
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
function getGitDiffStat(cwd) {
|
|
2123
|
-
try {
|
|
2124
|
-
const output = execFileSync("git", ["diff", "HEAD", "--numstat"], {
|
|
2125
|
-
cwd,
|
|
2126
|
-
...EXEC_OPTS,
|
|
2127
|
-
timeout: 1e4
|
|
2128
|
-
}).trim();
|
|
2129
|
-
if (!output) return { filesChanged: 0, insertions: 0, deletions: 0 };
|
|
2130
|
-
let insertions = 0;
|
|
2131
|
-
let deletions = 0;
|
|
2132
|
-
let filesChanged = 0;
|
|
2133
|
-
for (const line of output.split("\n")) {
|
|
2134
|
-
const parts = line.split(" ");
|
|
2135
|
-
if (parts[0] === "-") {
|
|
2136
|
-
filesChanged++;
|
|
2137
|
-
continue;
|
|
2138
|
-
}
|
|
2139
|
-
insertions += parseInt(parts[0], 10) || 0;
|
|
2140
|
-
deletions += parseInt(parts[1], 10) || 0;
|
|
2141
|
-
filesChanged++;
|
|
2142
|
-
}
|
|
2143
|
-
return { filesChanged, insertions, deletions };
|
|
2144
|
-
} catch {
|
|
2145
|
-
return null;
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
function getGitDiffFull(cwd) {
|
|
2149
|
-
try {
|
|
2150
|
-
const stat = getGitDiffStat(cwd);
|
|
2151
|
-
if (!stat) return null;
|
|
2152
|
-
const MAX_DIFF_SIZE = 500 * 1024;
|
|
2153
|
-
let rawDiff = execFileSync("git", ["diff", "HEAD", "-U3"], {
|
|
2154
|
-
cwd,
|
|
2155
|
-
...EXEC_OPTS,
|
|
2156
|
-
timeout: 15e3,
|
|
2157
|
-
maxBuffer: MAX_DIFF_SIZE * 2
|
|
2158
|
-
});
|
|
2159
|
-
if (rawDiff.length > MAX_DIFF_SIZE) {
|
|
2160
|
-
rawDiff = rawDiff.slice(0, MAX_DIFF_SIZE) + "\n\n... diff truncated (too large) ...\n";
|
|
2161
|
-
}
|
|
2162
|
-
const numstatOutput = execFileSync("git", ["diff", "HEAD", "--numstat"], {
|
|
2163
|
-
cwd,
|
|
2164
|
-
...EXEC_OPTS,
|
|
2165
|
-
timeout: 1e4
|
|
2166
|
-
}).trim();
|
|
2167
|
-
const fileStats = /* @__PURE__ */ new Map();
|
|
2168
|
-
if (numstatOutput) {
|
|
2169
|
-
for (const line of numstatOutput.split("\n")) {
|
|
2170
|
-
const parts = line.split(" ");
|
|
2171
|
-
if (parts.length >= 3) {
|
|
2172
|
-
const ins = parts[0] === "-" ? 0 : parseInt(parts[0], 10) || 0;
|
|
2173
|
-
const del = parts[1] === "-" ? 0 : parseInt(parts[1], 10) || 0;
|
|
2174
|
-
fileStats.set(parts.slice(2).join(" "), { insertions: ins, deletions: del });
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
const fileDiffs = [];
|
|
2179
|
-
const diffSections = rawDiff.split(/^diff --git /m).filter(Boolean);
|
|
2180
|
-
for (const section of diffSections) {
|
|
2181
|
-
const fullSection = "diff --git " + section;
|
|
2182
|
-
const plusMatch = fullSection.match(/^\+\+\+ b\/(.+)$/m);
|
|
2183
|
-
const minusMatch = fullSection.match(/^--- a\/(.+)$/m);
|
|
2184
|
-
const filePath = plusMatch?.[1] || minusMatch?.[1]?.replace(/^\/dev\/null$/, "") || "unknown";
|
|
2185
|
-
let status = "modified";
|
|
2186
|
-
if (fullSection.includes("--- /dev/null")) {
|
|
2187
|
-
status = "added";
|
|
2188
|
-
} else if (fullSection.includes("+++ /dev/null")) {
|
|
2189
|
-
status = "deleted";
|
|
2190
|
-
} else if (fullSection.includes("rename from")) {
|
|
2191
|
-
status = "renamed";
|
|
2192
|
-
}
|
|
2193
|
-
const stats = fileStats.get(filePath) || { insertions: 0, deletions: 0 };
|
|
2194
|
-
fileDiffs.push({
|
|
2195
|
-
filePath,
|
|
2196
|
-
status,
|
|
2197
|
-
insertions: stats.insertions,
|
|
2198
|
-
deletions: stats.deletions,
|
|
2199
|
-
diff: fullSection
|
|
2200
|
-
});
|
|
2201
|
-
}
|
|
2202
|
-
return { stat, files: fileDiffs };
|
|
2203
|
-
} catch {
|
|
2204
|
-
return null;
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
function gitCommit(cwd, message, includeUnstaged) {
|
|
2208
|
-
try {
|
|
2209
|
-
if (includeUnstaged) {
|
|
2210
|
-
execFileSync("git", ["add", "-A"], { cwd, ...EXEC_OPTS, timeout: 1e4 });
|
|
2211
|
-
}
|
|
2212
|
-
execFileSync("git", ["commit", "-m", message], {
|
|
2213
|
-
cwd,
|
|
2214
|
-
...EXEC_OPTS,
|
|
2215
|
-
timeout: 15e3
|
|
2216
|
-
});
|
|
2217
|
-
return { success: true };
|
|
2218
|
-
} catch (err) {
|
|
2219
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2220
|
-
return { success: false, error: msg };
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
function gitPush(cwd) {
|
|
2224
|
-
try {
|
|
2225
|
-
execFileSync("git", ["push"], { cwd, ...EXEC_OPTS, timeout: 3e4 });
|
|
2226
|
-
return { success: true };
|
|
2227
|
-
} catch (err) {
|
|
2228
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2229
|
-
return { success: false, error: msg };
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2232
|
-
function listWorktrees(projectPath) {
|
|
2233
|
-
try {
|
|
2234
|
-
const output = execFileSync("git", ["worktree", "list", "--porcelain"], {
|
|
2235
|
-
cwd: projectPath,
|
|
2236
|
-
...EXEC_OPTS,
|
|
2237
|
-
timeout: 5e3
|
|
2238
|
-
}).trim();
|
|
2239
|
-
if (!output) return [];
|
|
2240
|
-
const worktrees = [];
|
|
2241
|
-
const blocks = output.split("\n\n");
|
|
2242
|
-
for (const block of blocks) {
|
|
2243
|
-
const lines = block.split("\n");
|
|
2244
|
-
const wtPath = lines.find((l) => l.startsWith("worktree "))?.replace("worktree ", "");
|
|
2245
|
-
const branchLine = lines.find((l) => l.startsWith("branch "));
|
|
2246
|
-
const branch = branchLine?.replace("branch refs/heads/", "") || "detached";
|
|
2247
|
-
if (wtPath) {
|
|
2248
|
-
worktrees.push({ path: wtPath, branch, isMain: worktrees.length === 0 });
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
return worktrees;
|
|
2252
|
-
} catch {
|
|
2253
|
-
return [];
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
// src/tools/git.ts
|
|
2258
|
-
function registerGitTools(server) {
|
|
2259
|
-
server.tool(
|
|
2260
|
-
"list_branches",
|
|
2261
|
-
"List git branches for a project",
|
|
2262
|
-
{ project_path: V.absolutePath.describe("Absolute path to project directory") },
|
|
2263
|
-
async (args) => {
|
|
2264
|
-
try {
|
|
2265
|
-
const local = listBranches(args.project_path);
|
|
2266
|
-
const current = getGitBranch(args.project_path);
|
|
2267
|
-
return {
|
|
2268
|
-
content: [{ type: "text", text: JSON.stringify({ current, branches: local }, null, 2) }]
|
|
2269
|
-
};
|
|
2270
|
-
} catch (err) {
|
|
2271
|
-
return {
|
|
2272
|
-
content: [{ type: "text", text: `Error listing branches: ${err}` }],
|
|
2273
|
-
isError: true
|
|
2274
|
-
};
|
|
1985
|
+
if (args.task_id) {
|
|
1986
|
+
const runs = listWorkflowRunsByTask(args.task_id, args.limit ?? 20);
|
|
1987
|
+
return { content: [{ type: "text", text: JSON.stringify(runs, null, 2) }] };
|
|
2275
1988
|
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
"list_remote_branches",
|
|
2280
|
-
"List remote git branches for a project",
|
|
2281
|
-
{ project_path: V.absolutePath.describe("Absolute path to project directory") },
|
|
2282
|
-
async (args) => {
|
|
2283
|
-
try {
|
|
2284
|
-
const remote = listRemoteBranches(args.project_path);
|
|
2285
|
-
return { content: [{ type: "text", text: JSON.stringify(remote, null, 2) }] };
|
|
2286
|
-
} catch (err) {
|
|
2287
|
-
return {
|
|
2288
|
-
content: [{ type: "text", text: `Error listing remote branches: ${err}` }],
|
|
2289
|
-
isError: true
|
|
2290
|
-
};
|
|
1989
|
+
if (args.workflow_id) {
|
|
1990
|
+
const runs = listWorkflowRuns(args.workflow_id, args.limit ?? 20);
|
|
1991
|
+
return { content: [{ type: "text", text: JSON.stringify(runs, null, 2) }] };
|
|
2291
1992
|
}
|
|
1993
|
+
return {
|
|
1994
|
+
content: [{ type: "text", text: "Error: provide either workflow_id or task_id" }],
|
|
1995
|
+
isError: true
|
|
1996
|
+
};
|
|
2292
1997
|
}
|
|
2293
1998
|
);
|
|
2294
1999
|
server.tool(
|
|
2295
|
-
"
|
|
2296
|
-
"Get
|
|
2297
|
-
{
|
|
2000
|
+
"get_workflow_schedule",
|
|
2001
|
+
"Get scheduler info for a workflow: execution log or next scheduled run. Requires the VibeGrid app to be running.",
|
|
2002
|
+
{
|
|
2003
|
+
workflow_id: V.id.optional().describe("Workflow ID (required for next_run, optional for log)"),
|
|
2004
|
+
info: z5.enum(["log", "next_run"]).optional().describe("What to retrieve (default: log)")
|
|
2005
|
+
},
|
|
2298
2006
|
async (args) => {
|
|
2007
|
+
const info = args.info ?? "log";
|
|
2299
2008
|
try {
|
|
2300
|
-
|
|
2301
|
-
|
|
2009
|
+
if (info === "next_run") {
|
|
2010
|
+
if (!args.workflow_id) {
|
|
2011
|
+
return {
|
|
2012
|
+
content: [{ type: "text", text: "Error: workflow_id is required for next_run" }],
|
|
2013
|
+
isError: true
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
const nextRun = await rpcCall("scheduler:getNextRun", args.workflow_id);
|
|
2302
2017
|
return {
|
|
2303
|
-
content: [
|
|
2018
|
+
content: [
|
|
2019
|
+
{
|
|
2020
|
+
type: "text",
|
|
2021
|
+
text: nextRun ? JSON.stringify({ nextRun }, null, 2) : "No scheduled run (workflow may be manual or disabled)"
|
|
2022
|
+
}
|
|
2023
|
+
]
|
|
2304
2024
|
};
|
|
2025
|
+
} else {
|
|
2026
|
+
const log2 = await rpcCall("scheduler:getLog", args.workflow_id);
|
|
2027
|
+
return { content: [{ type: "text", text: JSON.stringify(log2, null, 2) }] };
|
|
2305
2028
|
}
|
|
2306
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2307
|
-
} catch (err) {
|
|
2308
|
-
return { content: [{ type: "text", text: `Error getting diff: ${err}` }], isError: true };
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
);
|
|
2312
|
-
server.tool(
|
|
2313
|
-
"get_diff_stat",
|
|
2314
|
-
"Get a summary of git changes (files changed, insertions, deletions)",
|
|
2315
|
-
{ project_path: V.absolutePath.describe("Absolute path to project directory") },
|
|
2316
|
-
async (args) => {
|
|
2317
|
-
try {
|
|
2318
|
-
const result = getGitDiffStat(args.project_path);
|
|
2319
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2320
|
-
} catch (err) {
|
|
2321
|
-
return {
|
|
2322
|
-
content: [{ type: "text", text: `Error getting diff stat: ${err}` }],
|
|
2323
|
-
isError: true
|
|
2324
|
-
};
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
);
|
|
2328
|
-
server.tool(
|
|
2329
|
-
"git_commit",
|
|
2330
|
-
"Create a git commit",
|
|
2331
|
-
{
|
|
2332
|
-
project_path: V.absolutePath.describe("Absolute path to project directory"),
|
|
2333
|
-
message: V.description.describe("Commit message"),
|
|
2334
|
-
include_unstaged: z6.boolean().optional().describe("Stage all changes before committing")
|
|
2335
|
-
},
|
|
2336
|
-
async (args) => {
|
|
2337
|
-
try {
|
|
2338
|
-
const result = gitCommit(args.project_path, args.message, args.include_unstaged ?? false);
|
|
2339
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2340
|
-
} catch (err) {
|
|
2341
|
-
return {
|
|
2342
|
-
content: [{ type: "text", text: `Error committing: ${err}` }],
|
|
2343
|
-
isError: true
|
|
2344
|
-
};
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
);
|
|
2348
|
-
server.tool(
|
|
2349
|
-
"git_push",
|
|
2350
|
-
"Push commits to the remote repository",
|
|
2351
|
-
{ project_path: V.absolutePath.describe("Absolute path to project directory") },
|
|
2352
|
-
async (args) => {
|
|
2353
|
-
try {
|
|
2354
|
-
const result = gitPush(args.project_path);
|
|
2355
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2356
|
-
} catch (err) {
|
|
2357
|
-
return {
|
|
2358
|
-
content: [{ type: "text", text: `Error pushing: ${err}` }],
|
|
2359
|
-
isError: true
|
|
2360
|
-
};
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
|
-
);
|
|
2364
|
-
server.tool(
|
|
2365
|
-
"list_worktrees",
|
|
2366
|
-
"List git worktrees for a project",
|
|
2367
|
-
{ project_path: V.absolutePath.describe("Absolute path to project directory") },
|
|
2368
|
-
async (args) => {
|
|
2369
|
-
try {
|
|
2370
|
-
const worktrees = listWorktrees(args.project_path);
|
|
2371
|
-
return { content: [{ type: "text", text: JSON.stringify(worktrees, null, 2) }] };
|
|
2372
2029
|
} catch (err) {
|
|
2373
2030
|
return {
|
|
2374
|
-
content: [{ type: "text", text: `Error
|
|
2375
|
-
isError: true
|
|
2376
|
-
};
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
2379
|
-
);
|
|
2380
|
-
server.tool(
|
|
2381
|
-
"create_worktree",
|
|
2382
|
-
"Create a git worktree for a branch",
|
|
2383
|
-
{
|
|
2384
|
-
project_path: V.absolutePath.describe("Absolute path to project directory"),
|
|
2385
|
-
branch: V.shortText.describe("Branch name for the worktree")
|
|
2386
|
-
},
|
|
2387
|
-
async (args) => {
|
|
2388
|
-
try {
|
|
2389
|
-
const worktreePath = createWorktree(args.project_path, args.branch);
|
|
2390
|
-
return {
|
|
2391
|
-
content: [{ type: "text", text: JSON.stringify({ path: worktreePath }, null, 2) }]
|
|
2392
|
-
};
|
|
2393
|
-
} catch (err) {
|
|
2394
|
-
return {
|
|
2395
|
-
content: [{ type: "text", text: `Error creating worktree: ${err}` }],
|
|
2396
|
-
isError: true
|
|
2397
|
-
};
|
|
2398
|
-
}
|
|
2399
|
-
}
|
|
2400
|
-
);
|
|
2401
|
-
server.tool(
|
|
2402
|
-
"worktree_dirty",
|
|
2403
|
-
"Check if a worktree has uncommitted changes",
|
|
2404
|
-
{ worktree_path: V.absolutePath.describe("Absolute path to the worktree") },
|
|
2405
|
-
async (args) => {
|
|
2406
|
-
try {
|
|
2407
|
-
const dirty = isWorktreeDirty(args.worktree_path);
|
|
2408
|
-
return { content: [{ type: "text", text: JSON.stringify({ dirty }, null, 2) }] };
|
|
2409
|
-
} catch (err) {
|
|
2410
|
-
return {
|
|
2411
|
-
content: [{ type: "text", text: `Error checking worktree: ${err}` }],
|
|
2031
|
+
content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
|
|
2412
2032
|
isError: true
|
|
2413
2033
|
};
|
|
2414
2034
|
}
|
|
@@ -2429,8 +2049,8 @@ function registerConfigTools(server) {
|
|
|
2429
2049
|
}
|
|
2430
2050
|
|
|
2431
2051
|
// src/tools/workspaces.ts
|
|
2432
|
-
import
|
|
2433
|
-
import { z as
|
|
2052
|
+
import crypto3 from "crypto";
|
|
2053
|
+
import { z as z6 } from "zod";
|
|
2434
2054
|
function registerWorkspaceTools(server) {
|
|
2435
2055
|
server.tool("list_workspaces", "List all workspaces", async () => {
|
|
2436
2056
|
const workspaces = dbListWorkspaces();
|
|
@@ -2448,7 +2068,7 @@ function registerWorkspaceTools(server) {
|
|
|
2448
2068
|
const existing = dbListWorkspaces();
|
|
2449
2069
|
const maxOrder = existing.reduce((max, w) => Math.max(max, w.order), 0);
|
|
2450
2070
|
const workspace = {
|
|
2451
|
-
id:
|
|
2071
|
+
id: crypto3.randomUUID(),
|
|
2452
2072
|
name: args.name,
|
|
2453
2073
|
order: maxOrder + 1,
|
|
2454
2074
|
...args.icon && { icon: args.icon },
|
|
@@ -2467,7 +2087,7 @@ function registerWorkspaceTools(server) {
|
|
|
2467
2087
|
name: V.name.optional().describe("New name"),
|
|
2468
2088
|
icon: V.shortText.optional().describe("Lucide icon name"),
|
|
2469
2089
|
icon_color: V.hexColor.optional().describe("Hex color for icon"),
|
|
2470
|
-
order:
|
|
2090
|
+
order: z6.number().int().min(0).optional().describe("Sort order")
|
|
2471
2091
|
},
|
|
2472
2092
|
async (args) => {
|
|
2473
2093
|
const existing = dbListWorkspaces();
|
|
@@ -2517,7 +2137,6 @@ function registerWorkspaceTools(server) {
|
|
|
2517
2137
|
// src/server.ts
|
|
2518
2138
|
function createMcpServer(version) {
|
|
2519
2139
|
const server = new McpServer({ name: "vibegrid", version }, { capabilities: { tools: {} } });
|
|
2520
|
-
registerGitTools(server);
|
|
2521
2140
|
registerConfigTools(server);
|
|
2522
2141
|
registerProjectTools(server);
|
|
2523
2142
|
registerTaskTools(server);
|
|
@@ -2536,7 +2155,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
|
|
|
2536
2155
|
console.error = (...args) => _origError("[mcp:error]", ...args);
|
|
2537
2156
|
async function main() {
|
|
2538
2157
|
configManager.init();
|
|
2539
|
-
const version = true ? "0.
|
|
2158
|
+
const version = true ? "0.4.0-beta.1" : createRequire(import.meta.url)("../package.json").version;
|
|
2540
2159
|
const server = createMcpServer(version);
|
|
2541
2160
|
const transport = new StdioServerTransport();
|
|
2542
2161
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibegrid/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-beta.1",
|
|
4
4
|
"description": "VibeGrid MCP server — task management, git, and workflow tools for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
35
35
|
"libsql": "^0.5.22",
|
|
36
|
-
"pino": "^
|
|
36
|
+
"pino": "^10.3.1",
|
|
37
37
|
"ws": "^8.18.0",
|
|
38
38
|
"zod": "^3.23.0"
|
|
39
39
|
},
|