@vibegrid/mcp 0.4.0-beta.1 → 0.4.0-beta.10

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 +353 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -177,6 +177,7 @@ function createSchema() {
177
177
  agent_type TEXT PRIMARY KEY,
178
178
  command TEXT NOT NULL,
179
179
  args TEXT NOT NULL DEFAULT '[]',
180
+ headless_args TEXT,
180
181
  fallback_command TEXT,
181
182
  fallback_args TEXT
182
183
  );
@@ -237,7 +238,9 @@ function createSchema() {
237
238
  remote_host_label TEXT,
238
239
  hook_session_id TEXT,
239
240
  status_source TEXT,
240
- saved_at INTEGER
241
+ saved_at INTEGER,
242
+ sort_order INTEGER NOT NULL DEFAULT 0,
243
+ worktree_name TEXT
241
244
  );
242
245
 
243
246
  CREATE TABLE IF NOT EXISTS schedule_log (
@@ -317,8 +320,20 @@ function createSchema() {
317
320
 
318
321
  CREATE INDEX IF NOT EXISTS idx_session_logs_task ON session_logs(task_id);
319
322
  CREATE INDEX IF NOT EXISTS idx_session_logs_session ON session_logs(session_id);
323
+
324
+ CREATE TABLE IF NOT EXISTS session_events (
325
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
326
+ session_id TEXT NOT NULL,
327
+ event_type TEXT NOT NULL,
328
+ timestamp TEXT NOT NULL,
329
+ metadata TEXT
330
+ );
331
+
332
+ CREATE INDEX IF NOT EXISTS idx_session_events_session ON session_events(session_id, timestamp DESC);
333
+ CREATE INDEX IF NOT EXISTS idx_session_events_type ON session_events(event_type, timestamp DESC);
320
334
  `);
321
335
  migrateSchema(d);
336
+ verifySchema(d);
322
337
  }
323
338
  function migrateSchema(d) {
324
339
  const row = d.prepare("SELECT value FROM schema_meta WHERE key = 'schema_version'").get();
@@ -376,6 +391,106 @@ function migrateSchema(d) {
376
391
  })();
377
392
  logger_default.info("[database] migrated schema to version 2 (ssh credential vault)");
378
393
  }
394
+ if (version < 3) {
395
+ d.transaction(() => {
396
+ const sessionCols = d.prepare("PRAGMA table_info(sessions)").all();
397
+ if (!sessionCols.some((c) => c.name === "sort_order")) {
398
+ d.exec("ALTER TABLE sessions ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0");
399
+ }
400
+ d.prepare(
401
+ "INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '3')"
402
+ ).run();
403
+ })();
404
+ logger_default.info("[database] migrated schema to version 3 (session sort order)");
405
+ }
406
+ if (version < 4) {
407
+ d.transaction(() => {
408
+ const sessionCols = d.prepare("PRAGMA table_info(sessions)").all();
409
+ if (!sessionCols.some((c) => c.name === "worktree_name")) {
410
+ d.exec("ALTER TABLE sessions ADD COLUMN worktree_name TEXT");
411
+ }
412
+ d.prepare(
413
+ "INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '4')"
414
+ ).run();
415
+ })();
416
+ logger_default.info("[database] migrated schema to version 4 (worktree name)");
417
+ }
418
+ if (version < 5) {
419
+ d.transaction(() => {
420
+ const agentCols = d.prepare("PRAGMA table_info(agent_commands)").all();
421
+ if (!agentCols.some((c) => c.name === "headless_args")) {
422
+ d.exec("ALTER TABLE agent_commands ADD COLUMN headless_args TEXT");
423
+ }
424
+ d.prepare(
425
+ "INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '5')"
426
+ ).run();
427
+ })();
428
+ logger_default.info("[database] migrated schema to version 5 (headless args)");
429
+ }
430
+ if (version < 6) {
431
+ d.transaction(() => {
432
+ const sessionCols = d.prepare("PRAGMA table_info(sessions)").all();
433
+ if (!sessionCols.some((c) => c.name === "claude_session_id")) {
434
+ d.exec("ALTER TABLE sessions ADD COLUMN claude_session_id TEXT");
435
+ }
436
+ d.prepare(
437
+ "INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '6')"
438
+ ).run();
439
+ })();
440
+ logger_default.info("[database] migrated schema to version 6 (claude session id)");
441
+ }
442
+ }
443
+ function verifySchema(d) {
444
+ const expectedByTable = {
445
+ projects: [
446
+ {
447
+ column: "workspace_id",
448
+ ddl: "ALTER TABLE projects ADD COLUMN workspace_id TEXT NOT NULL DEFAULT 'personal'"
449
+ }
450
+ ],
451
+ workflows: [
452
+ {
453
+ column: "workspace_id",
454
+ ddl: "ALTER TABLE workflows ADD COLUMN workspace_id TEXT NOT NULL DEFAULT 'personal'"
455
+ }
456
+ ],
457
+ remote_hosts: [
458
+ { column: "auth_method", ddl: "ALTER TABLE remote_hosts ADD COLUMN auth_method TEXT" },
459
+ { column: "credential_id", ddl: "ALTER TABLE remote_hosts ADD COLUMN credential_id TEXT" },
460
+ {
461
+ column: "encrypted_password",
462
+ ddl: "ALTER TABLE remote_hosts ADD COLUMN encrypted_password TEXT"
463
+ }
464
+ ],
465
+ sessions: [
466
+ {
467
+ column: "sort_order",
468
+ ddl: "ALTER TABLE sessions ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"
469
+ },
470
+ { column: "worktree_name", ddl: "ALTER TABLE sessions ADD COLUMN worktree_name TEXT" },
471
+ { column: "claude_session_id", ddl: "ALTER TABLE sessions ADD COLUMN claude_session_id TEXT" }
472
+ ],
473
+ agent_commands: [
474
+ {
475
+ column: "headless_args",
476
+ ddl: "ALTER TABLE agent_commands ADD COLUMN headless_args TEXT"
477
+ }
478
+ ]
479
+ };
480
+ for (const [table, columns] of Object.entries(expectedByTable)) {
481
+ const existing = new Set(
482
+ d.prepare(`PRAGMA table_info(${table})`).all().map((c) => c.name)
483
+ );
484
+ for (const { column, ddl } of columns) {
485
+ if (existing.has(column)) continue;
486
+ try {
487
+ d.exec(ddl);
488
+ logger_default.warn(`[database] self-heal: added missing column ${table}.${column}`);
489
+ } catch (err) {
490
+ logger_default.error(`[database] self-heal: failed to add ${table}.${column}:`, err);
491
+ }
492
+ }
493
+ }
379
494
  }
380
495
  function loadConfig() {
381
496
  const d = getDb();
@@ -440,6 +555,12 @@ function loadDefaults(d) {
440
555
  },
441
556
  ...map.networkAccessEnabled !== void 0 && {
442
557
  networkAccessEnabled: map.networkAccessEnabled
558
+ },
559
+ ...map.showHeadlessAgents !== void 0 && {
560
+ showHeadlessAgents: map.showHeadlessAgents
561
+ },
562
+ ...map.headlessRetentionMinutes !== void 0 && {
563
+ headlessRetentionMinutes: map.headlessRetentionMinutes
443
564
  }
444
565
  };
445
566
  }
@@ -458,6 +579,7 @@ function loadAgentCommands(d) {
458
579
  result[r.agent_type] = {
459
580
  command: r.command,
460
581
  args: JSON.parse(r.args),
582
+ ...r.headless_args != null && { headlessArgs: JSON.parse(r.headless_args) },
461
583
  ...r.fallback_command != null && { fallbackCommand: r.fallback_command },
462
584
  ...r.fallback_args != null && { fallbackArgs: JSON.parse(r.fallback_args) }
463
585
  };
@@ -534,7 +656,7 @@ function saveConfig(config) {
534
656
  }
535
657
  d.prepare("DELETE FROM agent_commands").run();
536
658
  const insertAgent = d.prepare(
537
- "INSERT INTO agent_commands (agent_type, command, args, fallback_command, fallback_args) VALUES (?, ?, ?, ?, ?)"
659
+ "INSERT INTO agent_commands (agent_type, command, args, headless_args, fallback_command, fallback_args) VALUES (?, ?, ?, ?, ?, ?)"
538
660
  );
539
661
  if (config.agentCommands) {
540
662
  for (const [agentType, cmd] of Object.entries(config.agentCommands)) {
@@ -543,6 +665,7 @@ function saveConfig(config) {
543
665
  agentType,
544
666
  cmd.command,
545
667
  JSON.stringify(cmd.args),
668
+ cmd.headlessArgs ? JSON.stringify(cmd.headlessArgs) : null,
546
669
  cmd.fallbackCommand ?? null,
547
670
  cmd.fallbackArgs ? JSON.stringify(cmd.fallbackArgs) : null
548
671
  );
@@ -989,6 +1112,7 @@ var ConfigManager = class {
989
1112
  changeCallbacks = [];
990
1113
  dbWatcher = null;
991
1114
  debounceTimer = null;
1115
+ cachedConfig = null;
992
1116
  init() {
993
1117
  initDatabase();
994
1118
  }
@@ -997,8 +1121,11 @@ var ConfigManager = class {
997
1121
  closeDatabase();
998
1122
  }
999
1123
  loadConfig() {
1124
+ if (this.cachedConfig) return this.cachedConfig;
1000
1125
  try {
1001
- return loadConfig();
1126
+ const config = loadConfig();
1127
+ this.cachedConfig = config;
1128
+ return config;
1002
1129
  } catch (err) {
1003
1130
  logger_default.error("[config-manager] loadConfig failed, returning defaults:", err);
1004
1131
  return {
@@ -1018,6 +1145,7 @@ var ConfigManager = class {
1018
1145
  saveConfig(config) {
1019
1146
  try {
1020
1147
  saveConfig(config);
1148
+ this.cachedConfig = null;
1021
1149
  } catch (err) {
1022
1150
  logger_default.error("[config-manager] saveConfig failed:", err);
1023
1151
  throw err;
@@ -1029,6 +1157,7 @@ var ConfigManager = class {
1029
1157
  }
1030
1158
  /** Notify all registered callbacks (call after main-process config mutations) */
1031
1159
  notifyChanged() {
1160
+ this.cachedConfig = null;
1032
1161
  const config = this.loadConfig();
1033
1162
  for (const cb of this.changeCallbacks) {
1034
1163
  cb(config);
@@ -1465,26 +1594,128 @@ import { z as z4 } from "zod";
1465
1594
  import fs3 from "fs";
1466
1595
  import path4 from "path";
1467
1596
  import os3 from "os";
1597
+ import { execFileSync } from "child_process";
1468
1598
  import { WebSocket } from "ws";
1469
1599
  var PORT_FILE = path4.join(os3.homedir(), ".vibegrid", "ws-port");
1470
1600
  var TIMEOUT_MS = 1e4;
1601
+ var IS_WIN = process.platform === "win32";
1602
+ var PORT_FILE_MISSING_MSG = IS_WIN ? `VibeGrid port file not found (~/.vibegrid/ws-port).
1603
+ The app may be running but the port file was deleted (e.g. by another instance shutting down).
1604
+ To fix, find the VibeGrid process and its listening port:
1605
+ powershell -c "Get-NetTCPConnection -State Listen -OwningProcess (Get-Process VibeGrid).Id | Select LocalPort"
1606
+ Then write the WS port to the file:
1607
+ echo {"port":<PORT>,"pid":<PID>} > %USERPROFILE%\\.vibegrid\\ws-port
1608
+ Or restart VibeGrid to regenerate it.` : `VibeGrid port file not found (~/.vibegrid/ws-port).
1609
+ The app may be running but the port file was deleted (e.g. by another instance shutting down).
1610
+ To fix, run: lsof -iTCP -sTCP:LISTEN -P | grep VibeGrid
1611
+ Then write the WS port (the one on *:<port>) to the file:
1612
+ echo '{"port":<PORT>,"pid":<PID>}' > ~/.vibegrid/ws-port
1613
+ Or restart VibeGrid to regenerate it.`;
1614
+ var PORT_FILE_INVALID_MSG = `VibeGrid port file exists but contains invalid data (~/.vibegrid/ws-port).
1615
+ Delete it and restart VibeGrid, or overwrite it with the correct port:
1616
+ ${IS_WIN ? "del %USERPROFILE%\\.vibegrid\\ws-port" : "rm ~/.vibegrid/ws-port"}`;
1471
1617
  var rpcId = 0;
1618
+ var cachedPort = null;
1619
+ var cacheTimestamp = 0;
1620
+ var CACHE_TTL_MS = 5e3;
1621
+ var EXEC_OPTS = {
1622
+ encoding: "utf-8",
1623
+ timeout: 5e3,
1624
+ stdio: ["pipe", "pipe", "pipe"]
1625
+ };
1626
+ function discoverPort() {
1627
+ try {
1628
+ if (IS_WIN) {
1629
+ const taskOut = execFileSync(
1630
+ "tasklist",
1631
+ ["/FI", "IMAGENAME eq VibeGrid.exe", "/FO", "CSV", "/NH"],
1632
+ EXEC_OPTS
1633
+ );
1634
+ const pidMatch = taskOut.match(/"VibeGrid\.exe","(\d+)"/);
1635
+ if (!pidMatch) return null;
1636
+ const pid = pidMatch[1];
1637
+ const lines = execFileSync("netstat", ["-ano"], EXEC_OPTS).split("\n");
1638
+ let fallback = null;
1639
+ for (const line of lines) {
1640
+ if (!line.includes("LISTENING") || !line.trim().endsWith(pid)) continue;
1641
+ const m = line.match(/(?:0\.0\.0\.0|127\.0\.0\.1):(\d+)/);
1642
+ if (!m) continue;
1643
+ if (line.includes("0.0.0.0")) return parseInt(m[1], 10);
1644
+ fallback ??= parseInt(m[1], 10);
1645
+ }
1646
+ return fallback;
1647
+ } else {
1648
+ const lines = execFileSync("lsof", ["-iTCP", "-sTCP:LISTEN", "-P", "-n"], EXEC_OPTS).split(
1649
+ "\n"
1650
+ );
1651
+ let fallback = null;
1652
+ for (const line of lines) {
1653
+ if (!line.includes("VibeGrid")) continue;
1654
+ if (line.includes("*:")) {
1655
+ const m = line.match(/\*:(\d+)/);
1656
+ if (m) return parseInt(m[1], 10);
1657
+ }
1658
+ if (!fallback) {
1659
+ const m = line.match(/:(\d+)\s/);
1660
+ if (m) fallback = parseInt(m[1], 10);
1661
+ }
1662
+ }
1663
+ return fallback;
1664
+ }
1665
+ } catch {
1666
+ }
1667
+ return null;
1668
+ }
1669
+ function discoverAndHeal() {
1670
+ const now = Date.now();
1671
+ if (cachedPort && now - cacheTimestamp < CACHE_TTL_MS) return { port: cachedPort };
1672
+ const discovered = discoverPort();
1673
+ cachedPort = discovered;
1674
+ cacheTimestamp = now;
1675
+ if (discovered) {
1676
+ try {
1677
+ fs3.mkdirSync(path4.dirname(PORT_FILE), { recursive: true });
1678
+ fs3.writeFileSync(PORT_FILE, JSON.stringify({ port: discovered }), "utf-8");
1679
+ } catch {
1680
+ }
1681
+ return { port: discovered };
1682
+ }
1683
+ return { port: null, reason: "missing" };
1684
+ }
1472
1685
  function readPort() {
1473
1686
  try {
1474
1687
  const raw = fs3.readFileSync(PORT_FILE, "utf-8").trim();
1475
- const port = parseInt(raw, 10);
1476
- return Number.isFinite(port) && port > 0 ? port : null;
1688
+ if (!raw) return { port: null, reason: "invalid" };
1689
+ if (raw.startsWith("{")) {
1690
+ const parsed = JSON.parse(raw);
1691
+ const p2 = parsed?.port;
1692
+ const pid = parsed?.pid;
1693
+ if (typeof p2 !== "number" || !Number.isFinite(p2) || p2 <= 0) {
1694
+ return { port: null, reason: "invalid" };
1695
+ }
1696
+ if (typeof pid === "number" && Number.isInteger(pid) && pid > 0) {
1697
+ try {
1698
+ process.kill(pid, 0);
1699
+ } catch (err) {
1700
+ if (err.code === "EPERM") return { port: p2 };
1701
+ return discoverAndHeal();
1702
+ }
1703
+ }
1704
+ return { port: p2 };
1705
+ }
1706
+ const p = parseInt(raw, 10);
1707
+ return Number.isFinite(p) && p > 0 ? { port: p } : { port: null, reason: "invalid" };
1477
1708
  } catch {
1478
- return null;
1709
+ return discoverAndHeal();
1479
1710
  }
1480
1711
  }
1481
1712
  async function rpcCall(method, params) {
1482
- const port = readPort();
1483
- if (!port) {
1484
- throw new Error("VibeGrid app is not running. Start VibeGrid to use session management tools.");
1713
+ const result = readPort();
1714
+ if (!result.port) {
1715
+ throw new Error(result.reason === "invalid" ? PORT_FILE_INVALID_MSG : PORT_FILE_MISSING_MSG);
1485
1716
  }
1486
1717
  return new Promise((resolve, reject) => {
1487
- const ws = new WebSocket(`ws://127.0.0.1:${port}/ws`);
1718
+ const ws = new WebSocket(`ws://127.0.0.1:${result.port}/ws`);
1488
1719
  const id = ++rpcId;
1489
1720
  const timer = setTimeout(() => {
1490
1721
  ws.close();
@@ -1514,12 +1745,12 @@ async function rpcCall(method, params) {
1514
1745
  });
1515
1746
  }
1516
1747
  async function rpcNotify(method, params) {
1517
- const port = readPort();
1518
- if (!port) {
1519
- throw new Error("VibeGrid app is not running. Start VibeGrid to use session management tools.");
1748
+ const result = readPort();
1749
+ if (!result.port) {
1750
+ throw new Error(result.reason === "invalid" ? PORT_FILE_INVALID_MSG : PORT_FILE_MISSING_MSG);
1520
1751
  }
1521
1752
  return new Promise((resolve, reject) => {
1522
- const ws = new WebSocket(`ws://127.0.0.1:${port}/ws`);
1753
+ const ws = new WebSocket(`ws://127.0.0.1:${result.port}/ws`);
1523
1754
  ws.on("open", () => {
1524
1755
  ws.send(JSON.stringify({ jsonrpc: "2.0", method, params }));
1525
1756
  ws.close();
@@ -1755,12 +1986,12 @@ function registerSessionTools(server) {
1755
1986
  "Send input to a running terminal session. Requires the VibeGrid app to be running.",
1756
1987
  {
1757
1988
  id: V.id.describe("Session ID"),
1758
- data: z4.string().max(5e4, "Data must be 50000 characters or less").describe("Data to write (text input to send to the agent)")
1989
+ data: z4.string().max(5e4, "Data must be 50000 characters or less").describe("Data to write (text input to send to the agent)"),
1990
+ raw: z4.boolean().optional().describe("Send data as-is without appending carriage return (for raw terminal control)")
1759
1991
  },
1760
1992
  async (args) => {
1761
1993
  try {
1762
- const trimmed = args.data.replace(/[\r\n]+$/, "");
1763
- const data = trimmed + "\r";
1994
+ const data = args.raw ? args.data : args.data.replace(/[\r\n]+$/, "") + "\r";
1764
1995
  await rpcNotify("terminal:write", { id: args.id, data });
1765
1996
  return { content: [{ type: "text", text: `Wrote to session: ${args.id}` }] };
1766
1997
  } catch (err) {
@@ -1776,6 +2007,109 @@ function registerSessionTools(server) {
1776
2007
  }
1777
2008
  }
1778
2009
  );
2010
+ const KEY_MAP = {
2011
+ enter: "\r",
2012
+ escape: "\x1B",
2013
+ esc: "\x1B",
2014
+ tab: " ",
2015
+ "shift+tab": "\x1B[Z",
2016
+ up: "\x1B[A",
2017
+ down: "\x1B[B",
2018
+ left: "\x1B[D",
2019
+ right: "\x1B[C",
2020
+ backspace: "\x7F",
2021
+ delete: "\x1B[3~",
2022
+ home: "\x1B[H",
2023
+ end: "\x1B[F",
2024
+ "ctrl+c": "",
2025
+ "ctrl+d": "",
2026
+ "ctrl+x": "",
2027
+ "ctrl+z": ""
2028
+ };
2029
+ server.tool(
2030
+ "send_key",
2031
+ "Send a single keystroke or key combo to a terminal session without appending Enter. Use for TUI interactions like selecting menu options (1, 2, y, n), pressing Escape, Ctrl+C, arrow keys, etc.",
2032
+ {
2033
+ id: V.id.describe("Session ID"),
2034
+ key: z4.string().min(1).max(20).describe(
2035
+ "Key to send: single char (1, y, n), named key (enter, escape, tab, up, down, left, right, backspace, delete, home, end), or combo (ctrl+c, ctrl+d, ctrl+x, ctrl+z, shift+tab)"
2036
+ )
2037
+ },
2038
+ async (args) => {
2039
+ const key = args.key.toLowerCase().trim();
2040
+ let data = KEY_MAP[key];
2041
+ if (!data) {
2042
+ const ctrlMatch = key.match(/^ctrl\+([a-z])$/);
2043
+ if (ctrlMatch) {
2044
+ data = String.fromCharCode(ctrlMatch[1].toUpperCase().charCodeAt(0) - 64);
2045
+ } else if (args.key.length === 1) {
2046
+ data = args.key;
2047
+ } else {
2048
+ return {
2049
+ content: [
2050
+ {
2051
+ type: "text",
2052
+ text: `Unknown key: "${args.key}". Supported: single chars (1, y, n), named keys (${Object.keys(KEY_MAP).join(", ")}), or ctrl+<letter>.`
2053
+ }
2054
+ ],
2055
+ isError: true
2056
+ };
2057
+ }
2058
+ }
2059
+ try {
2060
+ await rpcNotify("terminal:write", { id: args.id, data });
2061
+ return {
2062
+ content: [{ type: "text", text: `Sent key "${args.key}" to session: ${args.id}` }]
2063
+ };
2064
+ } catch (err) {
2065
+ return {
2066
+ content: [
2067
+ {
2068
+ type: "text",
2069
+ text: `Error sending key to terminal: ${err instanceof Error ? err.message : err}`
2070
+ }
2071
+ ],
2072
+ isError: true
2073
+ };
2074
+ }
2075
+ }
2076
+ );
2077
+ server.tool(
2078
+ "list_session_events",
2079
+ "List session lifecycle events (created, exited, task_linked, renamed, archived, unarchived). Use for post-mortem analysis and multi-agent coordination.",
2080
+ {
2081
+ session_id: V.id.optional().describe("Filter by session ID"),
2082
+ event_type: z4.enum(["created", "exited", "task_linked", "renamed", "archived", "unarchived"]).optional().describe("Filter by event type"),
2083
+ limit: z4.number().int().min(1).max(200).optional().describe("Max events to return (default: 50)")
2084
+ },
2085
+ async (args) => {
2086
+ try {
2087
+ let events;
2088
+ if (args.session_id) {
2089
+ events = await rpcCall("sessionEvent:listBySession", {
2090
+ sessionId: args.session_id,
2091
+ limit: args.limit ?? 50
2092
+ });
2093
+ } else {
2094
+ events = await rpcCall("sessionEvent:list", {
2095
+ eventType: args.event_type,
2096
+ limit: args.limit ?? 50
2097
+ });
2098
+ }
2099
+ return { content: [{ type: "text", text: JSON.stringify(events, null, 2) }] };
2100
+ } catch (err) {
2101
+ return {
2102
+ content: [
2103
+ {
2104
+ type: "text",
2105
+ text: `Error listing session events: ${err instanceof Error ? err.message : err}`
2106
+ }
2107
+ ],
2108
+ isError: true
2109
+ };
2110
+ }
2111
+ }
2112
+ );
1779
2113
  }
1780
2114
 
1781
2115
  // src/tools/workflows.ts
@@ -1900,7 +2234,7 @@ function registerWorkflowTools(server) {
1900
2234
  const workflow = {
1901
2235
  id: crypto2.randomUUID(),
1902
2236
  name: args.name,
1903
- icon: args.icon ?? "zap",
2237
+ icon: args.icon ?? "Zap",
1904
2238
  iconColor: args.icon_color ?? "#6366f1",
1905
2239
  nodes,
1906
2240
  edges,
@@ -2155,7 +2489,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
2155
2489
  console.error = (...args) => _origError("[mcp:error]", ...args);
2156
2490
  async function main() {
2157
2491
  configManager.init();
2158
- const version = true ? "0.4.0-beta.1" : createRequire(import.meta.url)("../package.json").version;
2492
+ const version = true ? "0.4.0-beta.10" : createRequire(import.meta.url)("../package.json").version;
2159
2493
  const server = createMcpServer(version);
2160
2494
  const transport = new StdioServerTransport();
2161
2495
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibegrid/mcp",
3
- "version": "0.4.0-beta.1",
3
+ "version": "0.4.0-beta.10",
4
4
  "description": "VibeGrid MCP server — task management, git, and workflow tools for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",