@vailent/pulse-mcp 1.7.0 → 1.8.1

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 +113 -14
  2. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -41675,6 +41675,51 @@ function cleanEscapedString(input) {
41675
41675
  return matched[1].replace(doubleQuoteRegExp, "'");
41676
41676
  }
41677
41677
 
41678
+ // lib/work-item-state.ts
41679
+ var PRE_DEV_PHASES = ["discovery", "framing", "prototyping", "deep_design"];
41680
+ function normalizeWorkItemState(updates, current) {
41681
+ const result = { ...updates };
41682
+ const status = result.status ?? current?.status;
41683
+ const phase = result.phase ?? current?.phase;
41684
+ const track = result.track ?? current?.track;
41685
+ const step = result.implementation_step ?? current?.implementation_step;
41686
+ const hasBlocker = result.has_blocker ?? current?.has_blocker;
41687
+ if (result.status === "shipped") {
41688
+ result.track = "dev";
41689
+ result.phase = "implementation";
41690
+ result.implementation_step = "deploy";
41691
+ result.has_blocker = false;
41692
+ result.blocker_text = null;
41693
+ }
41694
+ if (result.status === "missed" || result.status === "killed" || result.status === "resolved") {
41695
+ result.has_blocker = false;
41696
+ result.blocker_text = null;
41697
+ }
41698
+ if ("phase" in result && result.phase !== "implementation") {
41699
+ result.implementation_step = null;
41700
+ if (PRE_DEV_PHASES.includes(result.phase)) {
41701
+ result.track = "pre_dev";
41702
+ }
41703
+ }
41704
+ if (result.phase === "implementation") {
41705
+ result.track = "dev";
41706
+ const effectiveStep = result.implementation_step ?? current?.implementation_step;
41707
+ if (!effectiveStep && !("implementation_step" in result)) {
41708
+ result.implementation_step = "build";
41709
+ }
41710
+ }
41711
+ if ("implementation_step" in result && result.implementation_step != null) {
41712
+ result.phase = "implementation";
41713
+ result.track = "dev";
41714
+ }
41715
+ if (result.has_blocker === true) {
41716
+ if (status === "shipped" || status === "missed") {
41717
+ result.status = "in_progress";
41718
+ }
41719
+ }
41720
+ return result;
41721
+ }
41722
+
41678
41723
  // tools/features.ts
41679
41724
  async function handleFeatures(params) {
41680
41725
  const supabase = getAdminClient();
@@ -41758,10 +41803,12 @@ async function handleFeatures(params) {
41758
41803
  prdFileName: "prd_file_name",
41759
41804
  prdSummary: "prd_summary"
41760
41805
  };
41761
- const updates = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
41806
+ const raw = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
41762
41807
  for (const [key, col] of Object.entries(allowedFields)) {
41763
- if (params[key] !== void 0) updates[col] = params[key];
41808
+ if (params[key] !== void 0) raw[col] = params[key];
41764
41809
  }
41810
+ const { data: current } = await supabase.from("work_items").select("status, phase, track, implementation_step, has_blocker").eq("id", featureId).single();
41811
+ const updates = normalizeWorkItemState(raw, current || void 0);
41765
41812
  const { error: error2 } = await supabase.from("work_items").update(updates).eq("id", featureId);
41766
41813
  if (error2) return err(error2.message);
41767
41814
  return ok({ id: featureId, ...updates }, `Updated feature ${featureId}`);
@@ -41907,6 +41954,7 @@ async function handleRequests(params) {
41907
41954
  const title = params.title;
41908
41955
  if (!podId || !title?.trim()) return err("podId and title are required", "MISSING_PARAM");
41909
41956
  const { data, error: error2 } = await supabase.from("work_items").insert({
41957
+ id: `f-${Date.now().toString(36)}`,
41910
41958
  title: title.trim(),
41911
41959
  description: params.description || null,
41912
41960
  pod_id: podId,
@@ -41986,6 +42034,7 @@ async function handleBugs(params) {
41986
42034
  const title = params.title;
41987
42035
  if (!podId || !title?.trim()) return err("podId and title are required", "MISSING_PARAM");
41988
42036
  const { data, error: error2 } = await supabase.from("work_items").insert({
42037
+ id: `f-${Date.now().toString(36)}`,
41989
42038
  title: title.trim(),
41990
42039
  description: params.description || null,
41991
42040
  pod_id: podId,
@@ -42001,20 +42050,21 @@ async function handleBugs(params) {
42001
42050
  case "update": {
42002
42051
  const bugId = params.bugId;
42003
42052
  if (!bugId) return err("bugId is required", "MISSING_PARAM");
42004
- const { data: currentBug } = await supabase.from("work_items").select("status, week_start, title, pod_id").eq("id", bugId).single();
42005
- const updates = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
42006
- if (params.title !== void 0) updates.title = params.title;
42007
- if (params.description !== void 0) updates.description = params.description;
42008
- if (params.priority !== void 0) updates.priority = params.priority;
42009
- if (params.status !== void 0) updates.status = params.status;
42010
- if (params.podId !== void 0) updates.pod_id = params.podId;
42011
- if (params.projectName !== void 0) updates.project_name = params.projectName;
42053
+ const { data: currentBug } = await supabase.from("work_items").select("status, phase, track, implementation_step, has_blocker, week_start, title, pod_id").eq("id", bugId).single();
42054
+ const raw = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
42055
+ if (params.title !== void 0) raw.title = params.title;
42056
+ if (params.description !== void 0) raw.description = params.description;
42057
+ if (params.priority !== void 0) raw.priority = params.priority;
42058
+ if (params.status !== void 0) raw.status = params.status;
42059
+ if (params.podId !== void 0) raw.pod_id = params.podId;
42060
+ if (params.projectName !== void 0) raw.project_name = params.projectName;
42012
42061
  let autoPromoted = false;
42013
42062
  if (currentBug?.status === "open" && !currentBug.week_start && !params.status) {
42014
- updates.status = "in_progress";
42015
- updates.week_start = getCurrentWeekStart();
42063
+ raw.status = "in_progress";
42064
+ raw.week_start = getCurrentWeekStart();
42016
42065
  autoPromoted = true;
42017
42066
  }
42067
+ const updates = normalizeWorkItemState(raw, currentBug || void 0);
42018
42068
  const { error: error2 } = await supabase.from("work_items").update(updates).eq("id", bugId);
42019
42069
  if (error2) return err(error2.message);
42020
42070
  if (autoPromoted) {
@@ -47230,6 +47280,45 @@ async function handleTeam(params) {
47230
47280
  }
47231
47281
  }
47232
47282
 
47283
+ // tools/search.ts
47284
+ async function handleSearch(params) {
47285
+ const text = params.text;
47286
+ if (!text?.trim()) return err("text is required", "MISSING_PARAM");
47287
+ const limit = params.limit || 5;
47288
+ const threshold = params.threshold || 0.78;
47289
+ const apiKey = process.env.OPENAI_API_KEY;
47290
+ if (!apiKey) return err("OPENAI_API_KEY not configured \u2014 cannot perform similarity search", "CONFIG_ERROR");
47291
+ let embedding;
47292
+ try {
47293
+ const res = await fetch("https://api.openai.com/v1/embeddings", {
47294
+ method: "POST",
47295
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
47296
+ body: JSON.stringify({ model: "text-embedding-3-small", input: text.slice(0, 8e3) })
47297
+ });
47298
+ if (!res.ok) return err(`OpenAI error: ${res.status}`, "API_ERROR");
47299
+ const data2 = await res.json();
47300
+ embedding = data2.data[0].embedding;
47301
+ } catch (e) {
47302
+ return err(`Embedding failed: ${e}`, "API_ERROR");
47303
+ }
47304
+ const supabase = getAdminClient();
47305
+ const { data, error: error2 } = await supabase.rpc("match_work_items", {
47306
+ query_embedding: JSON.stringify(embedding),
47307
+ match_threshold: threshold,
47308
+ match_count: limit
47309
+ });
47310
+ if (error2) return err(error2.message);
47311
+ const results = data || [];
47312
+ return ok(
47313
+ results.map((r) => ({
47314
+ ...r,
47315
+ similarity: Math.round(r.similarity * 100) / 100
47316
+ })),
47317
+ `${results.length} similar item(s) found`,
47318
+ results.length
47319
+ );
47320
+ }
47321
+
47233
47322
  // server.ts
47234
47323
  try {
47235
47324
  await import("./config-V5TD57MJ.js");
@@ -47281,7 +47370,7 @@ server.tool(
47281
47370
  );
47282
47371
  server.tool(
47283
47372
  "pulse_features",
47284
- "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.",
47373
+ "Manage features and tasks. Actions: list (filter by pod/workstream/week/status/size), get, create (feature or task via size param), update (auto-corrects state: shipped\u2192dev/implementation/deploy, phase changes update track/step), delete, add_note (auto-promotes backlog bugs to this week), reorder.",
47285
47374
  {
47286
47375
  action: external_exports.enum(["list", "get", "create", "update", "delete", "add_note", "reorder"]),
47287
47376
  featureId: external_exports.string().optional().describe("Feature ID"),
@@ -47349,7 +47438,7 @@ server.tool(
47349
47438
  );
47350
47439
  server.tool(
47351
47440
  "pulse_bugs",
47352
- "Track bugs. Actions: list (open/in_progress bugs by pod), create, update (status/priority/podId/projectName), delete, schedule (set week_start \u2014 auto-promotes open\u2192in_progress).",
47441
+ "Track bugs. Actions: list (open/in_progress bugs by pod), create, update (auto-promotes backlog bugs to this week, auto-corrects state consistency), delete, schedule (set week_start \u2014 auto-promotes open\u2192in_progress).",
47353
47442
  {
47354
47443
  action: external_exports.enum(["list", "create", "update", "delete", "schedule"]),
47355
47444
  bugId: external_exports.string().optional().describe("Bug ID"),
@@ -47389,6 +47478,16 @@ server.tool(
47389
47478
  },
47390
47479
  async (params) => handleEvents(params)
47391
47480
  );
47481
+ server.tool(
47482
+ "pulse_search",
47483
+ "Semantic similarity search across all work items (bugs, features, requests, feedback). Uses embeddings to find related items. Great for checking duplicates before creating.",
47484
+ {
47485
+ text: external_exports.string().describe("Search text \u2014 describe the bug, feature, or issue to find similar items"),
47486
+ limit: external_exports.number().optional().describe("Max results (default 5)"),
47487
+ threshold: external_exports.number().optional().describe("Similarity threshold 0-1 (default 0.78)")
47488
+ },
47489
+ async (params) => handleSearch(params)
47490
+ );
47392
47491
  server.tool(
47393
47492
  "pulse_history",
47394
47493
  "Search historical items (shipped features, closed requests, bugs, feedback). Filters: type, pod, project, days.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vailent/pulse-mcp",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "Pulse MCP server — manage pods, features, workstreams, bugs, and more from Claude Code",
5
5
  "type": "module",
6
6
  "bin": {