@vibegrid/mcp 0.1.1 → 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.
- package/dist/index.js +57 -7
- package/package.json +6 -6
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)
|
|
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)
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibegrid/mcp",
|
|
3
|
-
"version": "0.1.
|
|
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",
|
|
7
7
|
"author": "Javier Canizalez <javier-canizalez@outlook.com>",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/jcanizalez/vibegrid.git",
|
|
10
|
+
"url": "git+https://github.com/jcanizalez/vibegrid.git",
|
|
11
11
|
"directory": "packages/mcp"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"task-management"
|
|
20
20
|
],
|
|
21
21
|
"bin": {
|
|
22
|
-
"vibegrid-mcp": "
|
|
22
|
+
"vibegrid-mcp": "dist/index.js"
|
|
23
23
|
},
|
|
24
24
|
"main": "./dist/index.js",
|
|
25
25
|
"files": [
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"zod": "^4.3.6"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@vibegrid/server": "
|
|
42
|
-
"@vibegrid/shared": "
|
|
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
|
+
}
|