@vailent/pulse-mcp 1.8.1 → 1.10.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.
- package/dist/server.js +180 -13
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -39920,7 +39920,9 @@ async function handlePods(params) {
|
|
|
39920
39920
|
if (params.name !== void 0) updates.name = params.name.trim();
|
|
39921
39921
|
if (params.color !== void 0) updates.color = params.color;
|
|
39922
39922
|
if (params.isActive !== void 0) updates.is_active = params.isActive;
|
|
39923
|
-
if (params.slackChannelId !== void 0)
|
|
39923
|
+
if (params.slackChannelId !== void 0) {
|
|
39924
|
+
updates.slack_channel_ids = params.slackChannelId ? [params.slackChannelId] : [];
|
|
39925
|
+
}
|
|
39924
39926
|
const { data, error: error2 } = await supabase.from("pods").update(updates).eq("id", podId).select().single();
|
|
39925
39927
|
if (error2) return err(error2.message);
|
|
39926
39928
|
return ok(data, `Updated pod: ${data?.name}`);
|
|
@@ -41869,35 +41871,102 @@ async function handleWorkstreams(params) {
|
|
|
41869
41871
|
const action = params.action;
|
|
41870
41872
|
switch (action) {
|
|
41871
41873
|
case "list": {
|
|
41872
|
-
let query = supabase.from("projects").select("
|
|
41873
|
-
if (params.podId) query = query.eq("pod_id", params.podId);
|
|
41874
|
+
let query = supabase.from("projects").select("*, project_pods(pod_id, is_primary)").order("name");
|
|
41874
41875
|
if (params.status) query = query.eq("status", params.status);
|
|
41875
41876
|
else query = query.eq("status", "active");
|
|
41876
41877
|
const { data, error: error2 } = await query;
|
|
41877
41878
|
if (error2) return err(error2.message);
|
|
41878
|
-
|
|
41879
|
+
let results = data || [];
|
|
41880
|
+
if (params.podId) {
|
|
41881
|
+
results = results.filter((p) => {
|
|
41882
|
+
const pp = p.project_pods || [];
|
|
41883
|
+
return pp.some((r) => r.pod_id === params.podId) || p.pod_id === params.podId;
|
|
41884
|
+
});
|
|
41885
|
+
}
|
|
41886
|
+
return ok(results, `${results.length} workstreams`, results.length);
|
|
41879
41887
|
}
|
|
41880
41888
|
case "get": {
|
|
41881
41889
|
const slug = params.slug;
|
|
41882
41890
|
if (!slug) return err("slug is required", "MISSING_PARAM");
|
|
41883
|
-
const { data, error: error2 } = await supabase.from("projects").select("
|
|
41891
|
+
const { data, error: error2 } = await supabase.from("projects").select("*, project_pods(pod_id, is_primary)").eq("slug", slug).single();
|
|
41884
41892
|
if (error2 || !data) return err("Workstream not found", "NOT_FOUND");
|
|
41885
41893
|
return ok(data, `Workstream: ${data.name}`);
|
|
41886
41894
|
}
|
|
41887
41895
|
case "create": {
|
|
41888
41896
|
const name = params.name;
|
|
41897
|
+
const podId = params.podId;
|
|
41898
|
+
const mode = params.mode;
|
|
41889
41899
|
if (!name?.trim()) return err("name is required", "MISSING_PARAM");
|
|
41890
|
-
const
|
|
41900
|
+
const trimmedName = name.trim();
|
|
41901
|
+
const { data: existing } = await supabase.from("projects").select("id, name, slug, project_pods(pod_id)").eq("name", trimmedName).maybeSingle();
|
|
41902
|
+
if (existing && !mode) {
|
|
41903
|
+
const existingPodIds = existing.project_pods?.map((r) => r.pod_id) || [];
|
|
41904
|
+
if (podId && existingPodIds.includes(podId)) {
|
|
41905
|
+
return ok(existing, `Workstream "${trimmedName}" already linked to this pod`);
|
|
41906
|
+
}
|
|
41907
|
+
return ok(
|
|
41908
|
+
{ conflict: true, existingProject: { id: existing.id, name: existing.name, slug: existing.slug, podIds: existingPodIds } },
|
|
41909
|
+
`Workstream "${trimmedName}" already exists in another pod. Use mode: "link" to share or mode: "create_new" to create a separate one.`
|
|
41910
|
+
);
|
|
41911
|
+
}
|
|
41912
|
+
if (existing && mode === "link") {
|
|
41913
|
+
if (!podId) return err("podId is required to link", "MISSING_PARAM");
|
|
41914
|
+
await supabase.from("project_pods").insert({ project_id: existing.id, pod_id: podId, is_primary: false });
|
|
41915
|
+
return ok(existing, `Linked "${trimmedName}" to pod`);
|
|
41916
|
+
}
|
|
41917
|
+
if (existing && mode === "create_new") {
|
|
41918
|
+
const { data: pod } = podId ? await supabase.from("pods").select("name").eq("id", podId).single() : { data: null };
|
|
41919
|
+
const suffix = pod?.name || podId || "";
|
|
41920
|
+
const newName = suffix ? `${trimmedName} (${suffix})` : trimmedName;
|
|
41921
|
+
const newSlug = newName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
41922
|
+
const { data: data2, error: error3 } = await supabase.from("projects").insert({
|
|
41923
|
+
name: newName,
|
|
41924
|
+
slug: newSlug,
|
|
41925
|
+
description: params.description || null,
|
|
41926
|
+
pod_id: podId || null,
|
|
41927
|
+
color: params.color || "#71717a",
|
|
41928
|
+
status: "active"
|
|
41929
|
+
}).select().single();
|
|
41930
|
+
if (error3) return err(error3.message);
|
|
41931
|
+
if (podId) await supabase.from("project_pods").insert({ project_id: data2.id, pod_id: podId, is_primary: true });
|
|
41932
|
+
return ok(data2, `Created separate workstream: ${newName}`);
|
|
41933
|
+
}
|
|
41934
|
+
const slug = trimmedName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
41891
41935
|
const { data, error: error2 } = await supabase.from("projects").insert({
|
|
41892
|
-
name:
|
|
41936
|
+
name: trimmedName,
|
|
41893
41937
|
slug,
|
|
41894
41938
|
description: params.description || null,
|
|
41895
|
-
pod_id:
|
|
41939
|
+
pod_id: podId || null,
|
|
41896
41940
|
color: params.color || "#71717a",
|
|
41897
41941
|
status: "active"
|
|
41898
41942
|
}).select().single();
|
|
41899
41943
|
if (error2) return err(error2.message);
|
|
41900
|
-
|
|
41944
|
+
if (podId) await supabase.from("project_pods").insert({ project_id: data.id, pod_id: podId, is_primary: true });
|
|
41945
|
+
return ok(data, `Created workstream: ${trimmedName}`);
|
|
41946
|
+
}
|
|
41947
|
+
case "link": {
|
|
41948
|
+
const slug = params.slug;
|
|
41949
|
+
const podId = params.podId;
|
|
41950
|
+
if (!slug || !podId) return err("slug and podId are required", "MISSING_PARAM");
|
|
41951
|
+
const { data: project } = await supabase.from("projects").select("id, name").eq("slug", slug).single();
|
|
41952
|
+
if (!project) return err("Workstream not found", "NOT_FOUND");
|
|
41953
|
+
const { error: error2 } = await supabase.from("project_pods").insert({
|
|
41954
|
+
project_id: project.id,
|
|
41955
|
+
pod_id: podId,
|
|
41956
|
+
is_primary: false
|
|
41957
|
+
});
|
|
41958
|
+
if (error2) return err(error2.message);
|
|
41959
|
+
return ok({ projectId: project.id, podId }, `Linked "${project.name}" to pod`);
|
|
41960
|
+
}
|
|
41961
|
+
case "unlink": {
|
|
41962
|
+
const slug = params.slug;
|
|
41963
|
+
const podId = params.podId;
|
|
41964
|
+
if (!slug || !podId) return err("slug and podId are required", "MISSING_PARAM");
|
|
41965
|
+
const { data: project } = await supabase.from("projects").select("id, name").eq("slug", slug).single();
|
|
41966
|
+
if (!project) return err("Workstream not found", "NOT_FOUND");
|
|
41967
|
+
const { error: error2 } = await supabase.from("project_pods").delete().eq("project_id", project.id).eq("pod_id", podId);
|
|
41968
|
+
if (error2) return err(error2.message);
|
|
41969
|
+
return ok({ projectId: project.id, podId }, `Unlinked "${project.name}" from pod`);
|
|
41901
41970
|
}
|
|
41902
41971
|
case "update": {
|
|
41903
41972
|
const slug = params.slug;
|
|
@@ -47319,6 +47388,87 @@ async function handleSearch(params) {
|
|
|
47319
47388
|
);
|
|
47320
47389
|
}
|
|
47321
47390
|
|
|
47391
|
+
// tools/milestones.ts
|
|
47392
|
+
async function handleMilestones(params) {
|
|
47393
|
+
const supabase = getAdminClient();
|
|
47394
|
+
const action = params.action;
|
|
47395
|
+
switch (action) {
|
|
47396
|
+
case "list": {
|
|
47397
|
+
const projectId = params.projectId;
|
|
47398
|
+
const slug = params.slug;
|
|
47399
|
+
let query = supabase.from("milestones").select("*").order("sort_order").order("target_date");
|
|
47400
|
+
if (projectId) {
|
|
47401
|
+
query = query.eq("project_id", projectId);
|
|
47402
|
+
} else if (slug) {
|
|
47403
|
+
const { data: project } = await supabase.from("projects").select("id").eq("slug", slug).single();
|
|
47404
|
+
if (!project) return err("Workstream not found", "NOT_FOUND");
|
|
47405
|
+
query = query.eq("project_id", project.id);
|
|
47406
|
+
} else {
|
|
47407
|
+
return err("projectId or slug is required", "MISSING_PARAM");
|
|
47408
|
+
}
|
|
47409
|
+
const { data, error: error2 } = await query;
|
|
47410
|
+
if (error2) return err(error2.message);
|
|
47411
|
+
return ok(data, `${(data || []).length} milestones`, (data || []).length);
|
|
47412
|
+
}
|
|
47413
|
+
case "create": {
|
|
47414
|
+
const title = params.title;
|
|
47415
|
+
if (!title?.trim()) return err("title is required", "MISSING_PARAM");
|
|
47416
|
+
let projectId = params.projectId;
|
|
47417
|
+
if (!projectId && params.slug) {
|
|
47418
|
+
const { data: project } = await supabase.from("projects").select("id").eq("slug", params.slug).single();
|
|
47419
|
+
if (!project) return err("Workstream not found", "NOT_FOUND");
|
|
47420
|
+
projectId = project.id;
|
|
47421
|
+
}
|
|
47422
|
+
if (!projectId) return err("projectId or slug is required", "MISSING_PARAM");
|
|
47423
|
+
const { data, error: error2 } = await supabase.from("milestones").insert({
|
|
47424
|
+
project_id: projectId,
|
|
47425
|
+
title: title.trim(),
|
|
47426
|
+
description: params.description || null,
|
|
47427
|
+
target_date: params.targetDate || null,
|
|
47428
|
+
status: "pending"
|
|
47429
|
+
}).select().single();
|
|
47430
|
+
if (error2) return err(error2.message);
|
|
47431
|
+
return ok(data, `Created milestone: ${title}`);
|
|
47432
|
+
}
|
|
47433
|
+
case "update": {
|
|
47434
|
+
const milestoneId = params.milestoneId;
|
|
47435
|
+
if (!milestoneId) return err("milestoneId is required", "MISSING_PARAM");
|
|
47436
|
+
const updates = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
47437
|
+
if (params.title !== void 0) updates.title = params.title;
|
|
47438
|
+
if (params.description !== void 0) updates.description = params.description;
|
|
47439
|
+
if (params.targetDate !== void 0) updates.target_date = params.targetDate;
|
|
47440
|
+
if (params.status !== void 0) {
|
|
47441
|
+
updates.status = params.status;
|
|
47442
|
+
updates.completed_at = params.status === "completed" ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
47443
|
+
}
|
|
47444
|
+
if (params.sortOrder !== void 0) updates.sort_order = params.sortOrder;
|
|
47445
|
+
const { error: error2 } = await supabase.from("milestones").update(updates).eq("id", milestoneId);
|
|
47446
|
+
if (error2) return err(error2.message);
|
|
47447
|
+
return ok({ id: milestoneId }, "Milestone updated");
|
|
47448
|
+
}
|
|
47449
|
+
case "complete": {
|
|
47450
|
+
const milestoneId = params.milestoneId;
|
|
47451
|
+
if (!milestoneId) return err("milestoneId is required", "MISSING_PARAM");
|
|
47452
|
+
const { error: error2 } = await supabase.from("milestones").update({
|
|
47453
|
+
status: "completed",
|
|
47454
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47455
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
47456
|
+
}).eq("id", milestoneId);
|
|
47457
|
+
if (error2) return err(error2.message);
|
|
47458
|
+
return ok({ id: milestoneId }, "Milestone completed");
|
|
47459
|
+
}
|
|
47460
|
+
case "delete": {
|
|
47461
|
+
const milestoneId = params.milestoneId;
|
|
47462
|
+
if (!milestoneId) return err("milestoneId is required", "MISSING_PARAM");
|
|
47463
|
+
const { error: error2 } = await supabase.from("milestones").delete().eq("id", milestoneId);
|
|
47464
|
+
if (error2) return err(error2.message);
|
|
47465
|
+
return ok({ deleted: milestoneId }, "Milestone deleted");
|
|
47466
|
+
}
|
|
47467
|
+
default:
|
|
47468
|
+
return err(`Unknown action: ${action}`, "INVALID_ACTION");
|
|
47469
|
+
}
|
|
47470
|
+
}
|
|
47471
|
+
|
|
47322
47472
|
// server.ts
|
|
47323
47473
|
try {
|
|
47324
47474
|
await import("./config-V5TD57MJ.js");
|
|
@@ -47399,18 +47549,35 @@ server.tool(
|
|
|
47399
47549
|
);
|
|
47400
47550
|
server.tool(
|
|
47401
47551
|
"pulse_workstreams",
|
|
47402
|
-
"Manage workstreams (project containers). Actions: list, get (by slug), create
|
|
47552
|
+
"Manage workstreams (project containers). Actions: list, get (by slug), create (detects name conflicts \u2014 use mode 'link' to share or 'create_new' for separate), link (add pod to existing workstream), unlink (remove pod from workstream), update, delete, get_suggestions.",
|
|
47403
47553
|
{
|
|
47404
|
-
action: external_exports.enum(["list", "get", "create", "update", "delete", "get_suggestions"]),
|
|
47554
|
+
action: external_exports.enum(["list", "get", "create", "update", "delete", "get_suggestions", "link", "unlink"]),
|
|
47405
47555
|
slug: external_exports.string().optional().describe("Workstream slug"),
|
|
47406
|
-
podId: external_exports.string().optional().describe("Pod ID filter"),
|
|
47556
|
+
podId: external_exports.string().optional().describe("Pod ID filter / target pod for link/unlink"),
|
|
47407
47557
|
name: external_exports.string().optional().describe("Workstream name"),
|
|
47408
47558
|
description: external_exports.string().optional().describe("Description"),
|
|
47409
47559
|
color: external_exports.string().optional().describe("Hex color"),
|
|
47410
|
-
status: external_exports.string().optional().describe("Status: active, archived, completed")
|
|
47560
|
+
status: external_exports.string().optional().describe("Status: active, archived, completed"),
|
|
47561
|
+
mode: external_exports.enum(["link", "create_new"]).optional().describe("When creating with a conflicting name: 'link' to share or 'create_new' for separate")
|
|
47411
47562
|
},
|
|
47412
47563
|
async (params) => handleWorkstreams(params)
|
|
47413
47564
|
);
|
|
47565
|
+
server.tool(
|
|
47566
|
+
"pulse_milestones",
|
|
47567
|
+
"Manage workstream milestones (key dates/goals). Actions: list (by projectId or slug), create, update, complete, delete.",
|
|
47568
|
+
{
|
|
47569
|
+
action: external_exports.enum(["list", "create", "update", "complete", "delete"]),
|
|
47570
|
+
projectId: external_exports.string().optional().describe("Project UUID"),
|
|
47571
|
+
slug: external_exports.string().optional().describe("Workstream slug (alternative to projectId)"),
|
|
47572
|
+
milestoneId: external_exports.string().optional().describe("Milestone ID (for update/complete/delete)"),
|
|
47573
|
+
title: external_exports.string().optional().describe("Milestone title"),
|
|
47574
|
+
description: external_exports.string().optional().describe("Description"),
|
|
47575
|
+
targetDate: external_exports.string().optional().describe("Target date (YYYY-MM-DD)"),
|
|
47576
|
+
status: external_exports.string().optional().describe("Status: pending, completed"),
|
|
47577
|
+
sortOrder: external_exports.number().optional().describe("Sort order")
|
|
47578
|
+
},
|
|
47579
|
+
async (params) => handleMilestones(params)
|
|
47580
|
+
);
|
|
47414
47581
|
server.tool(
|
|
47415
47582
|
"pulse_requests",
|
|
47416
47583
|
"Manage feature requests and feedback. Actions: list (by pod), create, triage (accept/decline/defer/convert/acknowledge).",
|