@vibegrid/mcp 0.1.2 → 0.1.3

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 +57 -7
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import "module";
4
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
6
 
6
7
  // ../server/src/config-manager.ts
@@ -1534,7 +1535,36 @@ var ptyManager = new PtyManager();
1534
1535
 
1535
1536
  // ../server/src/scheduler.ts
1536
1537
  import cron from "node-cron";
1538
+ import fs4 from "fs";
1539
+ import path4 from "path";
1540
+ import os4 from "os";
1537
1541
  import { EventEmitter as EventEmitter2 } from "events";
1542
+ var LOCK_DIR = path4.join(os4.homedir(), ".vibegrid");
1543
+ function acquireExecutionLock(workflowId) {
1544
+ const minuteKey = Math.floor(Date.now() / 6e4);
1545
+ const lockFile = path4.join(LOCK_DIR, `scheduler-${workflowId}-${minuteKey}.lock`);
1546
+ try {
1547
+ fs4.writeFileSync(lockFile, String(process.pid), { flag: "wx" });
1548
+ cleanStaleLocks(workflowId, minuteKey);
1549
+ return true;
1550
+ } catch {
1551
+ return false;
1552
+ }
1553
+ }
1554
+ function cleanStaleLocks(workflowId, currentKey) {
1555
+ try {
1556
+ const prefix = `scheduler-${workflowId}-`;
1557
+ for (const f of fs4.readdirSync(LOCK_DIR)) {
1558
+ if (f.startsWith(prefix) && f.endsWith(".lock")) {
1559
+ const key = parseInt(f.slice(prefix.length, -5), 10);
1560
+ if (!isNaN(key) && key < currentKey) {
1561
+ fs4.unlinkSync(path4.join(LOCK_DIR, f));
1562
+ }
1563
+ }
1564
+ }
1565
+ } catch {
1566
+ }
1567
+ }
1538
1568
  function getTriggerConfig(wf) {
1539
1569
  const triggerNode = wf.nodes.find((n) => n.type === "trigger");
1540
1570
  if (!triggerNode) return null;
@@ -1544,6 +1574,9 @@ var Scheduler = class extends EventEmitter2 {
1544
1574
  cronJobs = /* @__PURE__ */ new Map();
1545
1575
  timeouts = /* @__PURE__ */ new Map();
1546
1576
  syncSchedules(workflows) {
1577
+ logger_default.info(
1578
+ `[scheduler] syncing ${workflows.length} workflows (active crons: ${this.cronJobs.size}, timeouts: ${this.timeouts.size})`
1579
+ );
1547
1580
  for (const [id] of this.cronJobs) {
1548
1581
  const wf = workflows.find((w) => w.id === id);
1549
1582
  const trigger = wf ? getTriggerConfig(wf) : null;
@@ -1561,10 +1594,20 @@ var Scheduler = class extends EventEmitter2 {
1561
1594
  }
1562
1595
  }
1563
1596
  for (const wf of workflows) {
1564
- if (!wf.enabled) continue;
1597
+ if (!wf.enabled) {
1598
+ logger_default.info(`[scheduler] skipping disabled workflow "${wf.name}"`);
1599
+ continue;
1600
+ }
1565
1601
  const trigger = getTriggerConfig(wf);
1566
- if (!trigger) continue;
1602
+ if (!trigger) {
1603
+ logger_default.info(`[scheduler] no trigger node for workflow "${wf.name}"`);
1604
+ continue;
1605
+ }
1606
+ logger_default.info(`[scheduler] workflow "${wf.name}" trigger=${trigger.triggerType}`);
1567
1607
  if (trigger.triggerType === "recurring" && !this.cronJobs.has(wf.id)) {
1608
+ logger_default.info(
1609
+ `[scheduler] registering recurring workflow "${wf.name}" cron="${trigger.cron}" enabled=${wf.enabled}`
1610
+ );
1568
1611
  if (!cron.validate(trigger.cron)) {
1569
1612
  logger_default.error(
1570
1613
  `[scheduler] invalid cron expression for workflow "${wf.name}": ${trigger.cron}`
@@ -1604,6 +1647,12 @@ var Scheduler = class extends EventEmitter2 {
1604
1647
  }
1605
1648
  }
1606
1649
  executeWorkflow(workflowId) {
1650
+ if (!acquireExecutionLock(workflowId)) {
1651
+ logger_default.info(`[scheduler] skipping workflow ${workflowId} \u2014 already executed by another instance`);
1652
+ this.timeouts.delete(workflowId);
1653
+ return;
1654
+ }
1655
+ logger_default.info(`[scheduler] executing workflow ${workflowId}`);
1607
1656
  this.emit("client-message", IPC.SCHEDULER_EXECUTE, { workflowId });
1608
1657
  this.timeouts.delete(workflowId);
1609
1658
  }
@@ -1649,7 +1698,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1649
1698
 
1650
1699
  // src/tools/tasks.ts
1651
1700
  import crypto3 from "crypto";
1652
- import path4 from "path";
1701
+ import path5 from "path";
1653
1702
  import { z as z2 } from "zod";
1654
1703
 
1655
1704
  // src/validation.ts
@@ -1853,12 +1902,12 @@ function registerTaskTools(server, deps) {
1853
1902
  };
1854
1903
  }
1855
1904
  const cwd = args.cwd || process.cwd();
1856
- const normalizedCwd = path4.resolve(cwd);
1905
+ const normalizedCwd = path5.resolve(cwd);
1857
1906
  const projects = dbListProjects();
1858
1907
  let matchedProject = null;
1859
1908
  let matchLen = 0;
1860
1909
  for (const p of projects) {
1861
- const normalizedPath = path4.resolve(p.path);
1910
+ const normalizedPath = path5.resolve(p.path);
1862
1911
  if (normalizedCwd.startsWith(normalizedPath) && normalizedPath.length > matchLen) {
1863
1912
  matchedProject = p;
1864
1913
  matchLen = normalizedPath.length;
@@ -1886,7 +1935,7 @@ function registerTaskTools(server, deps) {
1886
1935
  let matchedTask = null;
1887
1936
  for (const t of projectTasks) {
1888
1937
  if (t.worktreePath) {
1889
- const normalizedWorktree = path4.resolve(t.worktreePath);
1938
+ const normalizedWorktree = path5.resolve(t.worktreePath);
1890
1939
  if (normalizedCwd.startsWith(normalizedWorktree)) {
1891
1940
  matchedTask = t;
1892
1941
  break;
@@ -2383,7 +2432,8 @@ async function main() {
2383
2432
  }
2384
2433
  ptyManager.setRemoteHosts(config.remoteHosts ?? []);
2385
2434
  scheduler.syncSchedules(config.workflows ?? []);
2386
- const server = createMcpServer({ configManager, ptyManager, scheduler }, "0.1.2");
2435
+ const version = true ? "0.1.3" : createRequire(import.meta.url)("../package.json").version;
2436
+ const server = createMcpServer({ configManager, ptyManager, scheduler }, version);
2387
2437
  const transport = new StdioServerTransport();
2388
2438
  await server.connect(transport);
2389
2439
  transport.onclose = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibegrid/mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "VibeGrid MCP server — task management, git, and workflow tools for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,10 +38,10 @@
38
38
  "zod": "^4.3.6"
39
39
  },
40
40
  "devDependencies": {
41
- "@vibegrid/server": "workspace:*",
42
- "@vibegrid/shared": "workspace:*",
41
+ "@vibegrid/server": "0.0.1",
42
+ "@vibegrid/shared": "0.0.1",
43
43
  "tsup": "^8.5.0",
44
44
  "tsx": "^4.19.4",
45
45
  "typescript": "^5.6.0"
46
46
  }
47
- }
47
+ }