@vailent/pulse-mcp 1.7.0 → 1.8.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 +111 -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}`);
@@ -42001,20 +42048,21 @@ async function handleBugs(params) {
42001
42048
  case "update": {
42002
42049
  const bugId = params.bugId;
42003
42050
  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;
42051
+ 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();
42052
+ const raw = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
42053
+ if (params.title !== void 0) raw.title = params.title;
42054
+ if (params.description !== void 0) raw.description = params.description;
42055
+ if (params.priority !== void 0) raw.priority = params.priority;
42056
+ if (params.status !== void 0) raw.status = params.status;
42057
+ if (params.podId !== void 0) raw.pod_id = params.podId;
42058
+ if (params.projectName !== void 0) raw.project_name = params.projectName;
42012
42059
  let autoPromoted = false;
42013
42060
  if (currentBug?.status === "open" && !currentBug.week_start && !params.status) {
42014
- updates.status = "in_progress";
42015
- updates.week_start = getCurrentWeekStart();
42061
+ raw.status = "in_progress";
42062
+ raw.week_start = getCurrentWeekStart();
42016
42063
  autoPromoted = true;
42017
42064
  }
42065
+ const updates = normalizeWorkItemState(raw, currentBug || void 0);
42018
42066
  const { error: error2 } = await supabase.from("work_items").update(updates).eq("id", bugId);
42019
42067
  if (error2) return err(error2.message);
42020
42068
  if (autoPromoted) {
@@ -47230,6 +47278,45 @@ async function handleTeam(params) {
47230
47278
  }
47231
47279
  }
47232
47280
 
47281
+ // tools/search.ts
47282
+ async function handleSearch(params) {
47283
+ const text = params.text;
47284
+ if (!text?.trim()) return err("text is required", "MISSING_PARAM");
47285
+ const limit = params.limit || 5;
47286
+ const threshold = params.threshold || 0.78;
47287
+ const apiKey = process.env.OPENAI_API_KEY;
47288
+ if (!apiKey) return err("OPENAI_API_KEY not configured \u2014 cannot perform similarity search", "CONFIG_ERROR");
47289
+ let embedding;
47290
+ try {
47291
+ const res = await fetch("https://api.openai.com/v1/embeddings", {
47292
+ method: "POST",
47293
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
47294
+ body: JSON.stringify({ model: "text-embedding-3-small", input: text.slice(0, 8e3) })
47295
+ });
47296
+ if (!res.ok) return err(`OpenAI error: ${res.status}`, "API_ERROR");
47297
+ const data2 = await res.json();
47298
+ embedding = data2.data[0].embedding;
47299
+ } catch (e) {
47300
+ return err(`Embedding failed: ${e}`, "API_ERROR");
47301
+ }
47302
+ const supabase = getAdminClient();
47303
+ const { data, error: error2 } = await supabase.rpc("match_work_items", {
47304
+ query_embedding: JSON.stringify(embedding),
47305
+ match_threshold: threshold,
47306
+ match_count: limit
47307
+ });
47308
+ if (error2) return err(error2.message);
47309
+ const results = data || [];
47310
+ return ok(
47311
+ results.map((r) => ({
47312
+ ...r,
47313
+ similarity: Math.round(r.similarity * 100) / 100
47314
+ })),
47315
+ `${results.length} similar item(s) found`,
47316
+ results.length
47317
+ );
47318
+ }
47319
+
47233
47320
  // server.ts
47234
47321
  try {
47235
47322
  await import("./config-V5TD57MJ.js");
@@ -47281,7 +47368,7 @@ server.tool(
47281
47368
  );
47282
47369
  server.tool(
47283
47370
  "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.",
47371
+ "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
47372
  {
47286
47373
  action: external_exports.enum(["list", "get", "create", "update", "delete", "add_note", "reorder"]),
47287
47374
  featureId: external_exports.string().optional().describe("Feature ID"),
@@ -47349,7 +47436,7 @@ server.tool(
47349
47436
  );
47350
47437
  server.tool(
47351
47438
  "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).",
47439
+ "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
47440
  {
47354
47441
  action: external_exports.enum(["list", "create", "update", "delete", "schedule"]),
47355
47442
  bugId: external_exports.string().optional().describe("Bug ID"),
@@ -47389,6 +47476,16 @@ server.tool(
47389
47476
  },
47390
47477
  async (params) => handleEvents(params)
47391
47478
  );
47479
+ server.tool(
47480
+ "pulse_search",
47481
+ "Semantic similarity search across all work items (bugs, features, requests, feedback). Uses embeddings to find related items. Great for checking duplicates before creating.",
47482
+ {
47483
+ text: external_exports.string().describe("Search text \u2014 describe the bug, feature, or issue to find similar items"),
47484
+ limit: external_exports.number().optional().describe("Max results (default 5)"),
47485
+ threshold: external_exports.number().optional().describe("Similarity threshold 0-1 (default 0.78)")
47486
+ },
47487
+ async (params) => handleSearch(params)
47488
+ );
47392
47489
  server.tool(
47393
47490
  "pulse_history",
47394
47491
  "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.0",
4
4
  "description": "Pulse MCP server — manage pods, features, workstreams, bugs, and more from Claude Code",
5
5
  "type": "module",
6
6
  "bin": {