@vibegrid/mcp 0.3.2 → 0.4.0-beta.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.
Files changed (2) hide show
  1. package/dist/index.js +91 -574
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1525,26 +1525,37 @@ var AGENT_TYPES3 = [
1525
1525
  function registerSessionTools(server) {
1526
1526
  server.tool(
1527
1527
  "list_sessions",
1528
- "List all active terminal sessions. Requires the VibeGrid app to be running.",
1528
+ 'List terminal sessions. Filter by status: "active" (running terminals), "recent" (past sessions), or "archived".',
1529
1529
  {
1530
- project_name: V.name.optional().describe("Filter by project name")
1530
+ filter: z4.enum(["active", "recent", "archived"]).optional().describe("Session filter (default: active)"),
1531
+ project_name: V.name.optional().describe("Filter by project name"),
1532
+ project_path: V.absolutePath.optional().describe("Filter by project path (for recent sessions)")
1531
1533
  },
1532
1534
  async (args) => {
1535
+ const filter = args.filter ?? "active";
1533
1536
  try {
1534
- let sessions = await rpcCall("terminal:listActive");
1535
- if (args.project_name) {
1536
- sessions = sessions.filter((s) => s.projectName === args.project_name);
1537
+ if (filter === "active") {
1538
+ let sessions = await rpcCall("terminal:listActive");
1539
+ if (args.project_name) {
1540
+ sessions = sessions.filter((s) => s.projectName === args.project_name);
1541
+ }
1542
+ const summary = sessions.map((s) => ({
1543
+ id: s.id,
1544
+ agentType: s.agentType,
1545
+ projectName: s.projectName,
1546
+ status: s.status,
1547
+ displayName: s.displayName,
1548
+ branch: s.branch,
1549
+ pid: s.pid
1550
+ }));
1551
+ return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
1552
+ } else if (filter === "recent") {
1553
+ const sessions = await rpcCall("sessions:getRecent", args.project_path);
1554
+ return { content: [{ type: "text", text: JSON.stringify(sessions, null, 2) }] };
1555
+ } else {
1556
+ const sessions = await rpcCall("session:listArchived");
1557
+ return { content: [{ type: "text", text: JSON.stringify(sessions, null, 2) }] };
1537
1558
  }
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
1559
  } catch (err) {
1549
1560
  return {
1550
1561
  content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
@@ -1554,41 +1565,8 @@ function registerSessionTools(server) {
1554
1565
  }
1555
1566
  );
1556
1567
  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
- } catch (err) {
1582
- return {
1583
- content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
1584
- isError: true
1585
- };
1586
- }
1587
- }
1588
- );
1589
- server.tool(
1590
- "launch_agent",
1591
- "Launch an AI agent in a new terminal session. Requires the VibeGrid app to be running.",
1568
+ "launch_session",
1569
+ "Launch an AI agent session (interactive terminal or headless). Requires the VibeGrid app to be running.",
1592
1570
  {
1593
1571
  agent_type: z4.enum(AGENT_TYPES3).describe("Agent type to launch"),
1594
1572
  project_name: V.name.describe("Project name"),
@@ -1596,7 +1574,8 @@ function registerSessionTools(server) {
1596
1574
  prompt: V.prompt.optional().describe("Initial prompt to send to the agent"),
1597
1575
  branch: V.shortText.optional().describe("Git branch to checkout"),
1598
1576
  use_worktree: z4.boolean().optional().describe("Create a git worktree"),
1599
- display_name: V.shortText.optional().describe("Display name for the session")
1577
+ display_name: V.shortText.optional().describe("Display name for the session"),
1578
+ headless: z4.boolean().optional().describe("Launch as headless (no UI) session")
1600
1579
  },
1601
1580
  async (args) => {
1602
1581
  const payload = {
@@ -1608,8 +1587,10 @@ function registerSessionTools(server) {
1608
1587
  ...args.use_worktree && { useWorktree: args.use_worktree },
1609
1588
  ...args.display_name && { displayName: args.display_name }
1610
1589
  };
1590
+ const rpcMethod = args.headless ? "headless:create" : "terminal:create";
1591
+ const label = args.headless ? "headless" : "terminal";
1611
1592
  try {
1612
- const session = await rpcCall("terminal:create", payload);
1593
+ const session = await rpcCall(rpcMethod, payload);
1613
1594
  return {
1614
1595
  content: [
1615
1596
  {
@@ -1633,7 +1614,7 @@ function registerSessionTools(server) {
1633
1614
  content: [
1634
1615
  {
1635
1616
  type: "text",
1636
- text: `Error launching agent: ${err instanceof Error ? err.message : err}`
1617
+ text: `Error launching ${label} agent: ${err instanceof Error ? err.message : err}`
1637
1618
  }
1638
1619
  ],
1639
1620
  isError: true
@@ -1642,95 +1623,24 @@ function registerSessionTools(server) {
1642
1623
  }
1643
1624
  );
1644
1625
  server.tool(
1645
- "launch_headless",
1646
- "Launch a headless (no UI) agent session. Requires the VibeGrid app to be running.",
1626
+ "kill_session",
1627
+ "Kill a terminal or headless session. Requires the VibeGrid app to be running.",
1647
1628
  {
1648
- agent_type: z4.enum(AGENT_TYPES3).describe("Agent type to launch"),
1649
- project_name: V.name.describe("Project name"),
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")
1629
+ id: V.id.describe("Session ID to kill"),
1630
+ headless: z4.boolean().optional().describe("Kill a headless session instead of a terminal")
1655
1631
  },
1656
1632
  async (args) => {
1657
- const payload = {
1658
- agentType: args.agent_type,
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
- };
1666
- try {
1667
- const session = await rpcCall("headless:create", payload);
1668
- return {
1669
- content: [
1670
- {
1671
- type: "text",
1672
- text: JSON.stringify(
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
- )
1683
- }
1684
- ]
1685
- };
1686
- } catch (err) {
1687
- return {
1688
- content: [
1689
- {
1690
- type: "text",
1691
- text: `Error launching headless agent: ${err instanceof Error ? err.message : err}`
1692
- }
1693
- ],
1694
- isError: true
1695
- };
1696
- }
1697
- }
1698
- );
1699
- server.tool(
1700
- "kill_session",
1701
- "Kill a terminal session. Requires the VibeGrid app to be running.",
1702
- { id: V.id.describe("Session ID to kill") },
1703
- async (args) => {
1704
- try {
1705
- await rpcCall("terminal:kill", args.id);
1706
- return { content: [{ type: "text", text: `Killed session: ${args.id}` }] };
1707
- } catch (err) {
1708
- return {
1709
- content: [
1710
- {
1711
- type: "text",
1712
- text: `Error killing session: ${err instanceof Error ? err.message : err}`
1713
- }
1714
- ],
1715
- isError: true
1716
- };
1717
- }
1718
- }
1719
- );
1720
- server.tool(
1721
- "kill_headless",
1722
- "Kill a headless agent session. Requires the VibeGrid app to be running.",
1723
- { id: V.id.describe("Headless session ID to kill") },
1724
- async (args) => {
1633
+ const rpcMethod = args.headless ? "headless:kill" : "terminal:kill";
1634
+ const label = args.headless ? "headless session" : "session";
1725
1635
  try {
1726
- await rpcCall("headless:kill", args.id);
1727
- return { content: [{ type: "text", text: `Killed headless session: ${args.id}` }] };
1636
+ await rpcCall(rpcMethod, args.id);
1637
+ return { content: [{ type: "text", text: `Killed ${label}: ${args.id}` }] };
1728
1638
  } catch (err) {
1729
1639
  return {
1730
1640
  content: [
1731
1641
  {
1732
1642
  type: "text",
1733
- text: `Error killing headless session: ${err instanceof Error ? err.message : err}`
1643
+ text: `Error killing ${label}: ${err instanceof Error ? err.message : err}`
1734
1644
  }
1735
1645
  ],
1736
1646
  isError: true
@@ -1747,7 +1657,9 @@ function registerSessionTools(server) {
1747
1657
  },
1748
1658
  async (args) => {
1749
1659
  try {
1750
- await rpcNotify("terminal:write", { id: args.id, data: args.data });
1660
+ const trimmed = args.data.replace(/[\r\n]+$/, "");
1661
+ const data = trimmed + "\r";
1662
+ await rpcNotify("terminal:write", { id: args.id, data });
1751
1663
  return { content: [{ type: "text", text: `Wrote to session: ${args.id}` }] };
1752
1664
  } catch (err) {
1753
1665
  return {
@@ -1955,460 +1867,66 @@ function registerWorkflowTools(server) {
1955
1867
  );
1956
1868
  server.tool(
1957
1869
  "list_workflow_runs",
1958
- "List execution history for a workflow",
1870
+ "List workflow execution history. Filter by workflow_id or task_id.",
1959
1871
  {
1960
- workflow_id: V.id.describe("Workflow ID"),
1872
+ workflow_id: V.id.optional().describe("Filter by workflow ID"),
1873
+ task_id: V.id.optional().describe("Filter by task ID (runs triggered by this task)"),
1961
1874
  limit: z5.number().int().min(1).max(100).optional().describe("Max results (default: 20)")
1962
1875
  },
1963
1876
  async (args) => {
1964
- const runs = listWorkflowRuns(args.workflow_id, args.limit ?? 20);
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);
1877
+ if (args.workflow_id && args.task_id) {
2007
1878
  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}` }],
1879
+ content: [{ type: "text", text: "Error: provide workflow_id or task_id, not both" }],
2018
1880
  isError: true
2019
1881
  };
2020
1882
  }
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
- };
1883
+ if (args.task_id) {
1884
+ const runs = listWorkflowRunsByTask(args.task_id, args.limit ?? 20);
1885
+ return { content: [{ type: "text", text: JSON.stringify(runs, null, 2) }] };
2275
1886
  }
2276
- }
2277
- );
2278
- server.tool(
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
- };
1887
+ if (args.workflow_id) {
1888
+ const runs = listWorkflowRuns(args.workflow_id, args.limit ?? 20);
1889
+ return { content: [{ type: "text", text: JSON.stringify(runs, null, 2) }] };
2291
1890
  }
1891
+ return {
1892
+ content: [{ type: "text", text: "Error: provide either workflow_id or task_id" }],
1893
+ isError: true
1894
+ };
2292
1895
  }
2293
1896
  );
2294
1897
  server.tool(
2295
- "get_diff",
2296
- "Get git diff for a project (staged and unstaged changes)",
2297
- { project_path: V.absolutePath.describe("Absolute path to project directory") },
1898
+ "get_workflow_schedule",
1899
+ "Get scheduler info for a workflow: execution log or next scheduled run. Requires the VibeGrid app to be running.",
1900
+ {
1901
+ workflow_id: V.id.optional().describe("Workflow ID (required for next_run, optional for log)"),
1902
+ info: z5.enum(["log", "next_run"]).optional().describe("What to retrieve (default: log)")
1903
+ },
2298
1904
  async (args) => {
1905
+ const info = args.info ?? "log";
2299
1906
  try {
2300
- const result = getGitDiffFull(args.project_path);
2301
- if (!result) {
1907
+ if (info === "next_run") {
1908
+ if (!args.workflow_id) {
1909
+ return {
1910
+ content: [{ type: "text", text: "Error: workflow_id is required for next_run" }],
1911
+ isError: true
1912
+ };
1913
+ }
1914
+ const nextRun = await rpcCall("scheduler:getNextRun", args.workflow_id);
2302
1915
  return {
2303
- content: [{ type: "text", text: "No changes detected or not a git repository" }]
1916
+ content: [
1917
+ {
1918
+ type: "text",
1919
+ text: nextRun ? JSON.stringify({ nextRun }, null, 2) : "No scheduled run (workflow may be manual or disabled)"
1920
+ }
1921
+ ]
2304
1922
  };
1923
+ } else {
1924
+ const log2 = await rpcCall("scheduler:getLog", args.workflow_id);
1925
+ return { content: [{ type: "text", text: JSON.stringify(log2, null, 2) }] };
2305
1926
  }
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
1927
  } catch (err) {
2321
1928
  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
- } catch (err) {
2373
- return {
2374
- content: [{ type: "text", text: `Error listing worktrees: ${err}` }],
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}` }],
1929
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : err}` }],
2412
1930
  isError: true
2413
1931
  };
2414
1932
  }
@@ -2429,8 +1947,8 @@ function registerConfigTools(server) {
2429
1947
  }
2430
1948
 
2431
1949
  // src/tools/workspaces.ts
2432
- import crypto4 from "crypto";
2433
- import { z as z7 } from "zod";
1950
+ import crypto3 from "crypto";
1951
+ import { z as z6 } from "zod";
2434
1952
  function registerWorkspaceTools(server) {
2435
1953
  server.tool("list_workspaces", "List all workspaces", async () => {
2436
1954
  const workspaces = dbListWorkspaces();
@@ -2448,7 +1966,7 @@ function registerWorkspaceTools(server) {
2448
1966
  const existing = dbListWorkspaces();
2449
1967
  const maxOrder = existing.reduce((max, w) => Math.max(max, w.order), 0);
2450
1968
  const workspace = {
2451
- id: crypto4.randomUUID(),
1969
+ id: crypto3.randomUUID(),
2452
1970
  name: args.name,
2453
1971
  order: maxOrder + 1,
2454
1972
  ...args.icon && { icon: args.icon },
@@ -2467,7 +1985,7 @@ function registerWorkspaceTools(server) {
2467
1985
  name: V.name.optional().describe("New name"),
2468
1986
  icon: V.shortText.optional().describe("Lucide icon name"),
2469
1987
  icon_color: V.hexColor.optional().describe("Hex color for icon"),
2470
- order: z7.number().int().min(0).optional().describe("Sort order")
1988
+ order: z6.number().int().min(0).optional().describe("Sort order")
2471
1989
  },
2472
1990
  async (args) => {
2473
1991
  const existing = dbListWorkspaces();
@@ -2517,7 +2035,6 @@ function registerWorkspaceTools(server) {
2517
2035
  // src/server.ts
2518
2036
  function createMcpServer(version) {
2519
2037
  const server = new McpServer({ name: "vibegrid", version }, { capabilities: { tools: {} } });
2520
- registerGitTools(server);
2521
2038
  registerConfigTools(server);
2522
2039
  registerProjectTools(server);
2523
2040
  registerTaskTools(server);
@@ -2536,7 +2053,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
2536
2053
  console.error = (...args) => _origError("[mcp:error]", ...args);
2537
2054
  async function main() {
2538
2055
  configManager.init();
2539
- const version = true ? "0.3.2" : createRequire(import.meta.url)("../package.json").version;
2056
+ const version = true ? "0.4.0-beta.0" : createRequire(import.meta.url)("../package.json").version;
2540
2057
  const server = createMcpServer(version);
2541
2058
  const transport = new StdioServerTransport();
2542
2059
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibegrid/mcp",
3
- "version": "0.3.2",
3
+ "version": "0.4.0-beta.0",
4
4
  "description": "VibeGrid MCP server — task management, git, and workflow tools for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",