@vailent/pulse-mcp 1.2.0 → 1.3.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/server.js +48 -16
  2. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -40498,14 +40498,32 @@ async function handlePodMembers(params) {
40498
40498
  return ok(data, `${(data || []).length} members in pod`, (data || []).length);
40499
40499
  }
40500
40500
  case "add": {
40501
- const displayName = params.displayName;
40502
- if (!displayName?.trim()) return err("displayName is required", "MISSING_PARAM");
40503
- const initials = params.initials || displayName.split(" ").map((w) => w[0]).join("").toUpperCase().slice(0, 2);
40501
+ const userId = params.userId;
40502
+ let displayName = params.displayName;
40503
+ let initials = params.initials;
40504
+ let role = params.role || "engineer";
40505
+ if (userId) {
40506
+ const { data: user } = await supabase.from("users").select("full_name, job_title").eq("id", userId).single();
40507
+ if (!user) return err("User not found", "NOT_FOUND");
40508
+ if (!displayName) displayName = user.full_name;
40509
+ if (!initials) {
40510
+ initials = displayName.split(" ").map((w) => w[0]).join("").toUpperCase().slice(0, 2);
40511
+ }
40512
+ if (!params.role) {
40513
+ const jobTitles = user.job_title || ["member"];
40514
+ role = jobTitles.includes("product_lead") ? "product_lead" : jobTitles.includes("designer") ? "designer" : "engineer";
40515
+ }
40516
+ }
40517
+ if (!displayName?.trim()) return err("displayName is required (or provide userId)", "MISSING_PARAM");
40518
+ if (!initials) {
40519
+ initials = displayName.split(" ").map((w) => w[0]).join("").toUpperCase().slice(0, 2);
40520
+ }
40504
40521
  const { data, error: error2 } = await supabase.from("pod_members").insert({
40505
40522
  pod_id: podId,
40523
+ user_id: userId || null,
40506
40524
  display_name: displayName.trim(),
40507
40525
  initials,
40508
- role: params.role || "engineer"
40526
+ role
40509
40527
  }).select().single();
40510
40528
  if (error2) return err(error2.message);
40511
40529
  return ok(data, `Added ${displayName} to pod`);
@@ -40598,7 +40616,7 @@ function getGitEmail(scope) {
40598
40616
  async function lookupByEmail(supabase, email3) {
40599
40617
  const { data } = await supabase.from("users").select("id, email, full_name, job_title").eq("email", email3).limit(1).single();
40600
40618
  if (!data) return null;
40601
- return { id: data.id, email: data.email, fullName: data.full_name, jobTitle: data.job_title || "member" };
40619
+ return { id: data.id, email: data.email, fullName: data.full_name, jobTitle: data.job_title || ["member"] };
40602
40620
  }
40603
40621
 
40604
40622
  // tools/features.ts
@@ -40674,7 +40692,10 @@ async function handleFeatures(params) {
40674
40692
  weekStart: "week_start",
40675
40693
  size: "size",
40676
40694
  projectName: "project_name",
40677
- sortOrder: "sort_order"
40695
+ sortOrder: "sort_order",
40696
+ prdText: "prd_text",
40697
+ prdFileUrl: "prd_file_url",
40698
+ prdFileName: "prd_file_name"
40678
40699
  };
40679
40700
  const updates = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
40680
40701
  for (const [key, col] of Object.entries(allowedFields)) {
@@ -47470,7 +47491,9 @@ function formatWeekStart(date4) {
47470
47491
  async function handleBriefing(params) {
47471
47492
  const supabase = getAdminClient();
47472
47493
  const currentUser = await getCurrentUser();
47473
- const jobTitle = params.jobTitle || currentUser?.jobTitle || "engineer";
47494
+ const paramTitle = params.jobTitle;
47495
+ const jobTitles = paramTitle ? [paramTitle] : currentUser?.jobTitle || ["engineer"];
47496
+ const jobTitle = jobTitles.find((t) => t === "leadership") || jobTitles.find((t) => t === "product_lead") || jobTitles.find((t) => t === "designer") || jobTitles.find((t) => t === "engineer") || jobTitles.find((t) => t === "sales") || jobTitles[0] || "engineer";
47474
47497
  const podIds = params.podIds;
47475
47498
  const now = /* @__PURE__ */ new Date();
47476
47499
  const thisWeekStart = getWeekStart(now);
@@ -47539,7 +47562,7 @@ async function handleTeam(params) {
47539
47562
  id: u.id,
47540
47563
  email: u.email,
47541
47564
  fullName: u.full_name,
47542
- jobTitle: u.job_title || "member",
47565
+ jobTitle: u.job_title || ["member"],
47543
47566
  role: u.role,
47544
47567
  isActive: u.is_active,
47545
47568
  pods: userPods
@@ -47549,11 +47572,16 @@ async function handleTeam(params) {
47549
47572
  }
47550
47573
  case "update_role": {
47551
47574
  const userId = params.userId;
47552
- const jobTitle = params.jobTitle;
47553
- if (!userId || !jobTitle) return err("userId and jobTitle are required", "MISSING_PARAM");
47554
- const { error: error2 } = await supabase.from("users").update({ job_title: jobTitle, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", userId);
47575
+ const rawTitle = params.jobTitle;
47576
+ if (!userId || !rawTitle) return err("userId and jobTitle are required", "MISSING_PARAM");
47577
+ const titles = Array.isArray(rawTitle) ? rawTitle : [rawTitle];
47578
+ if (titles.length === 0) return err("At least one role is required", "INVALID_PARAM");
47579
+ const valid = ["product_lead", "engineer", "designer", "leadership", "sales", "member"];
47580
+ const invalid = titles.filter((t) => !valid.includes(t));
47581
+ if (invalid.length > 0) return err(`Invalid role(s): ${invalid.join(", ")}`, "INVALID_PARAM");
47582
+ const { error: error2 } = await supabase.from("users").update({ job_title: titles, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", userId);
47555
47583
  if (error2) return err(error2.message);
47556
- return ok({ userId, jobTitle }, `Updated role to ${jobTitle}`);
47584
+ return ok({ userId, jobTitle: titles }, `Updated roles to ${titles.join(", ")}`);
47557
47585
  }
47558
47586
  case "assign_pod": {
47559
47587
  const userId = params.userId;
@@ -47564,8 +47592,8 @@ async function handleTeam(params) {
47564
47592
  const { data: user } = await supabase.from("users").select("full_name, job_title").eq("id", userId).single();
47565
47593
  const displayName = user?.full_name || "Unknown";
47566
47594
  const initials = displayName.split(" ").map((w) => w[0]).join("").toUpperCase().slice(0, 2);
47567
- const roleMap = { product_lead: "product_lead", engineer: "engineer", designer: "designer" };
47568
- const podRole = roleMap[user?.job_title || ""] || "engineer";
47595
+ const jobTitles = user?.job_title || ["member"];
47596
+ const podRole = jobTitles.includes("product_lead") ? "product_lead" : jobTitles.includes("designer") ? "designer" : "engineer";
47569
47597
  await supabase.from("pod_members").insert({
47570
47598
  pod_id: podId,
47571
47599
  user_id: userId,
@@ -47609,6 +47637,7 @@ server.tool(
47609
47637
  action: external_exports4.enum(["list", "add", "update", "remove"]),
47610
47638
  podId: external_exports4.string().describe("Pod ID"),
47611
47639
  memberId: external_exports4.string().optional().describe("Member ID (for update/remove)"),
47640
+ userId: external_exports4.string().optional().describe("User ID \u2014 links pod member to a user (for add). Auto-populates name/initials/role from user."),
47612
47641
  displayName: external_exports4.string().optional().describe("Display name"),
47613
47642
  initials: external_exports4.string().optional().describe("2-letter initials"),
47614
47643
  role: external_exports4.string().optional().describe("Role: engineer, product_lead, designer")
@@ -47617,7 +47646,7 @@ server.tool(
47617
47646
  );
47618
47647
  server.tool(
47619
47648
  "pulse_features",
47620
- "Manage features and tasks. Actions: list (filter by pod/workstream/week/status/size), get, create (feature or task via size param), update (phase/step/status/blocker/week), delete, add_note, reorder.",
47649
+ "Manage features and tasks. Actions: list (filter by pod/workstream/week/status/size), get, create (feature or task via size param), update (phase/step/status/blocker/week/prdText/prdFileUrl), delete, add_note, reorder.",
47621
47650
  {
47622
47651
  action: external_exports4.enum(["list", "get", "create", "update", "delete", "add_note", "reorder"]),
47623
47652
  featureId: external_exports4.string().optional().describe("Feature ID"),
@@ -47634,6 +47663,9 @@ server.tool(
47634
47663
  blockerText: external_exports4.string().optional().describe("Blocker description"),
47635
47664
  sortOrder: external_exports4.number().optional().describe("Sort order for drag-to-reorder"),
47636
47665
  projectColor: external_exports4.string().optional().describe("Project color hex"),
47666
+ prdText: external_exports4.string().optional().describe("PRD text content (for update)"),
47667
+ prdFileUrl: external_exports4.string().optional().describe("PRD file URL (for update)"),
47668
+ prdFileName: external_exports4.string().optional().describe("PRD file name (for update)"),
47637
47669
  note: external_exports4.string().optional().describe("Note text (for add_note)"),
47638
47670
  items: external_exports4.array(external_exports4.object({ id: external_exports4.string(), sortOrder: external_exports4.number() })).optional().describe("Items to reorder"),
47639
47671
  limit: external_exports4.number().optional().describe("Max results")
@@ -47749,7 +47781,7 @@ server.tool(
47749
47781
  {
47750
47782
  action: external_exports4.enum(["list", "update_role", "assign_pod"]),
47751
47783
  userId: external_exports4.string().optional().describe("User ID"),
47752
- jobTitle: external_exports4.string().optional().describe("Job title: product_lead, engineer, designer, leadership, sales, member"),
47784
+ jobTitle: external_exports4.union([external_exports4.string(), external_exports4.array(external_exports4.string())]).optional().describe("Job title(s): product_lead, engineer, designer, leadership, sales, member. Accepts single string or array."),
47753
47785
  podId: external_exports4.string().optional().nullable().describe("Pod ID (null to remove from pods)")
47754
47786
  },
47755
47787
  async (params) => handleTeam(params)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vailent/pulse-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Pulse MCP server — manage pods, features, workstreams, bugs, and more from Claude Code",
5
5
  "type": "module",
6
6
  "bin": {