@papi-ai/server 0.7.54 → 0.7.55

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.
@@ -1065,7 +1065,7 @@ var init_install_id = __esm({
1065
1065
  function reportTelemetryEmitFailure(reason, ctx) {
1066
1066
  consecutiveTelemetryFailures += 1;
1067
1067
  console.error(
1068
- `[telemetry] emit failed (${reason}) tool=${ctx.toolName} event=${ctx.eventType} project=${ctx.projectId}`
1068
+ `[telemetry] emit failed (${reason}) tool=${ctx.toolName} event=${ctx.eventType} project=${ctx.projectId ?? "(none)"}`
1069
1069
  );
1070
1070
  if (consecutiveTelemetryFailures >= TELEMETRY_BLACKOUT_THRESHOLD) {
1071
1071
  console.error(
@@ -1561,18 +1561,19 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
1561
1561
  */
1562
1562
  emitTelemetry(event) {
1563
1563
  const ctx = { projectId: event.projectId, toolName: event.toolName, eventType: event.eventType };
1564
+ const payload = {
1565
+ toolName: event.toolName,
1566
+ eventType: event.eventType,
1567
+ metadata: event.metadata ?? {}
1568
+ };
1569
+ if (event.projectId) payload["projectId"] = event.projectId;
1564
1570
  fetch(`${this.endpoint}/telemetry`, {
1565
1571
  method: "POST",
1566
1572
  headers: {
1567
1573
  "Content-Type": "application/json",
1568
1574
  "Authorization": `Bearer ${this.apiKey}`
1569
1575
  },
1570
- body: JSON.stringify({
1571
- projectId: event.projectId,
1572
- toolName: event.toolName,
1573
- eventType: event.eventType,
1574
- metadata: event.metadata ?? {}
1575
- }),
1576
+ body: JSON.stringify(payload),
1576
1577
  signal: AbortSignal.timeout(5e3)
1577
1578
  }).then((res) => {
1578
1579
  if (res.ok) noteTelemetryEmitSuccess();
package/dist/index.js CHANGED
@@ -1103,7 +1103,7 @@ function isEnabled() {
1103
1103
  function reportTelemetryEmitFailure(reason, ctx) {
1104
1104
  consecutiveTelemetryFailures += 1;
1105
1105
  console.error(
1106
- `[telemetry] emit failed (${reason}) tool=${ctx.toolName} event=${ctx.eventType} project=${ctx.projectId}`
1106
+ `[telemetry] emit failed (${reason}) tool=${ctx.toolName} event=${ctx.eventType} project=${ctx.projectId ?? "(none)"}`
1107
1107
  );
1108
1108
  if (consecutiveTelemetryFailures >= TELEMETRY_BLACKOUT_THRESHOLD) {
1109
1109
  console.error(
@@ -1120,11 +1120,11 @@ function emitTelemetryEvent(event) {
1120
1120
  if (!apiKey) return;
1121
1121
  const endpoint = process.env["PAPI_DATA_ENDPOINT"] ?? DEFAULT_TELEMETRY_ENDPOINT;
1122
1122
  const body = {
1123
- projectId: event.project_id,
1124
1123
  toolName: event.tool_name,
1125
1124
  eventType: event.event_type,
1126
1125
  metadata: event.metadata ?? {}
1127
1126
  };
1127
+ if (event.project_id) body["projectId"] = event.project_id;
1128
1128
  const ctx = {
1129
1129
  projectId: event.project_id,
1130
1130
  toolName: event.tool_name,
@@ -1678,18 +1678,19 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
1678
1678
  */
1679
1679
  emitTelemetry(event) {
1680
1680
  const ctx = { projectId: event.projectId, toolName: event.toolName, eventType: event.eventType };
1681
+ const payload = {
1682
+ toolName: event.toolName,
1683
+ eventType: event.eventType,
1684
+ metadata: event.metadata ?? {}
1685
+ };
1686
+ if (event.projectId) payload["projectId"] = event.projectId;
1681
1687
  fetch(`${this.endpoint}/telemetry`, {
1682
1688
  method: "POST",
1683
1689
  headers: {
1684
1690
  "Content-Type": "application/json",
1685
1691
  "Authorization": `Bearer ${this.apiKey}`
1686
1692
  },
1687
- body: JSON.stringify({
1688
- projectId: event.projectId,
1689
- toolName: event.toolName,
1690
- eventType: event.eventType,
1691
- metadata: event.metadata ?? {}
1692
- }),
1693
+ body: JSON.stringify(payload),
1693
1694
  signal: AbortSignal.timeout(5e3)
1694
1695
  }).then((res) => {
1695
1696
  if (res.ok) noteTelemetryEmitSuccess();
@@ -8411,6 +8412,9 @@ function computeCycleEffort(cycleTaskRows, cycleReports) {
8411
8412
  deliveredPoints: cycleReports.reduce((s, r) => s + effortWeight(r.actualEffort || r.estimatedEffort), 0)
8412
8413
  };
8413
8414
  }
8415
+ function velocityPoints(v) {
8416
+ return v.deliveredPoints ?? v.effortPoints;
8417
+ }
8414
8418
  function computeSnapshotsFromBuildReports(reports, tasks) {
8415
8419
  const reportsByCycle = /* @__PURE__ */ new Map();
8416
8420
  for (const r of reports) {
@@ -8461,22 +8465,22 @@ function formatCycleMetrics(snapshots) {
8461
8465
  const avgPlanned = Math.round(
8462
8466
  recentVelocities.reduce((sum, v) => sum + v.effortPoints, 0) / recentVelocities.length * 10
8463
8467
  ) / 10;
8464
- const withDelivered = recentVelocities.filter((v) => v.deliveredPoints !== void 0);
8465
- const avgDelivered = withDelivered.length > 0 ? Math.round(withDelivered.reduce((sum, v) => sum + (v.deliveredPoints ?? 0), 0) / withDelivered.length * 10) / 10 : void 0;
8466
- lines.push("**Cycle Sizing \u2014 planned vs delivered effort points**");
8468
+ const avgDelivered = Math.round(
8469
+ recentVelocities.reduce((sum, v) => sum + velocityPoints(v), 0) / recentVelocities.length * 10
8470
+ ) / 10;
8471
+ const anyDelivered = recentVelocities.some((v) => v.deliveredPoints !== void 0);
8472
+ lines.push("**Velocity \u2014 delivered effort points (size the next cycle from this)**");
8467
8473
  lines.push(
8468
- `- Last ${recentVelocities.length} cycles: ` + recentVelocities.map((v) => v.deliveredPoints !== void 0 ? `S${v.cycle}=${v.effortPoints} planned/${v.deliveredPoints} delivered` : `S${v.cycle}=${v.effortPoints} planned`).join(", ")
8474
+ `- Last ${recentVelocities.length} cycles: ` + recentVelocities.map((v) => v.deliveredPoints !== void 0 ? `S${v.cycle}=${v.deliveredPoints} delivered/${v.effortPoints} planned` : `S${v.cycle}=${v.effortPoints} planned (no delivered data \u2014 using planned)`).join(", ")
8469
8475
  );
8470
- if (avgDelivered !== void 0) {
8476
+ lines.push(`- Velocity (avg delivered): ${avgDelivered} pts/cycle (XS=1, S=2, M=3, L=5, XL=8)`);
8477
+ if (anyDelivered) {
8471
8478
  const delta = Math.round((avgDelivered - avgPlanned) * 10) / 10;
8472
8479
  const sign = delta > 0 ? "+" : "";
8473
8480
  const read = delta === 0 ? "delivered matches planned" : delta > 0 ? "cycles cost MORE than scoped (under-scoping)" : "cycles cost LESS than scoped (over-scoping)";
8474
- lines.push(`- Average: ${avgPlanned} planned / ${avgDelivered} delivered (XS=1, S=2, M=3, L=5, XL=8)`);
8475
- lines.push(`- Scope accuracy: ${sign}${delta} pts/cycle \u2014 ${read}.`);
8476
- } else {
8477
- lines.push(`- Average: ${avgPlanned} planned effort points/cycle (XS=1, S=2, M=3, L=5, XL=8)`);
8481
+ lines.push(`- Scope accuracy (drift vs planned): planned averaged ${avgPlanned} pts/cycle \u2014 ${sign}${delta} pts, ${read}.`);
8478
8482
  }
8479
- lines.push(`- Size cycles on what the selected tasks actually require \u2014 planned is a reference, not a target.`);
8483
+ lines.push(`- Size cycles on what the selected tasks actually cost \u2014 delivered is the signal, planned is the estimate that was wrong.`);
8480
8484
  }
8481
8485
  return lines.join("\n");
8482
8486
  }
@@ -8490,7 +8494,9 @@ function formatDerivedMetrics(snapshots, backlogTasks) {
8490
8494
  const latest = recent[recent.length - 1];
8491
8495
  lines.push("**Cycle History (5-cycle avg)**");
8492
8496
  lines.push(`- Average: ${avg.toFixed(1)} tasks/cycle`);
8493
- lines.push(`- Latest (Cycle ${latest.cycle}): ${latest.completed} tasks, ${latest.effortPoints} effort points`);
8497
+ const latestDelivered = velocityPoints(latest);
8498
+ const plannedNote = latest.deliveredPoints !== void 0 && latest.deliveredPoints !== latest.effortPoints ? ` (planned ${latest.effortPoints})` : "";
8499
+ lines.push(`- Latest (Cycle ${latest.cycle}): ${latest.completed} tasks, ${latestDelivered} effort points delivered${plannedNote}`);
8494
8500
  }
8495
8501
  }
8496
8502
  const activeTasks = backlogTasks.filter(
@@ -14072,6 +14078,7 @@ function extractTrendPoints(snapshots) {
14072
14078
  matchRate: accuracyRow.matchRate,
14073
14079
  bias: accuracyRow.bias,
14074
14080
  effortPoints: velocityRow.effortPoints,
14081
+ deliveredPoints: velocityPoints(velocityRow),
14075
14082
  completed: velocityRow.completed
14076
14083
  });
14077
14084
  }
@@ -14085,7 +14092,8 @@ function generateValueReport(snapshots) {
14085
14092
  const first = recent[0];
14086
14093
  const last = recent[recent.length - 1];
14087
14094
  const matchRateDelta = last.matchRate - first.matchRate;
14088
- const effortDelta = last.effortPoints - first.effortPoints;
14095
+ const effortDelta = last.deliveredPoints - first.deliveredPoints;
14096
+ const plannedDelta = last.effortPoints - first.effortPoints;
14089
14097
  const completedDelta = last.completed - first.completed;
14090
14098
  const biasTrend = assessBiasTrend(first.bias, last.bias);
14091
14099
  const lines = [];
@@ -14094,7 +14102,8 @@ function generateValueReport(snapshots) {
14094
14102
  lines.push("");
14095
14103
  lines.push(`- **Scope accuracy:** ${first.matchRate}% \u2192 ${last.matchRate}% (${formatDelta(matchRateDelta, "pp")})`);
14096
14104
  lines.push(`- **Estimation bias:** ${formatBias(first.bias)} \u2192 ${formatBias(last.bias)} (${biasTrend})`);
14097
- lines.push(`- **Velocity:** ${first.effortPoints} \u2192 ${last.effortPoints} effort points/cycle (${formatDelta(effortDelta, "")})`);
14105
+ lines.push(`- **Velocity (delivered):** ${first.deliveredPoints} \u2192 ${last.deliveredPoints} effort points/cycle (${formatDelta(effortDelta, "")})`);
14106
+ lines.push(`- **Planned (for drift):** ${first.effortPoints} \u2192 ${last.effortPoints} effort points/cycle (${formatDelta(plannedDelta, "")})`);
14098
14107
  lines.push(`- **Throughput:** ${first.completed} \u2192 ${last.completed} tasks/cycle (${formatDelta(completedDelta, "")})`);
14099
14108
  return lines.join("\n");
14100
14109
  }
@@ -24992,7 +25001,7 @@ function computeHealthScore(cycleNumber, snapshots, activeTasks, decisionUsage)
24992
25001
  const recentSnaps = snapshots.slice(-3);
24993
25002
  const baselineSnaps = snapshots.slice(-10);
24994
25003
  if (recentSnaps.length > 0 && baselineSnaps.length > 0) {
24995
- const avg = (snaps) => snaps.reduce((s, sn) => s + (sn.velocity[0]?.effortPoints ?? 0), 0) / snaps.length;
25004
+ const avg = (snaps) => snaps.reduce((s, sn) => s + (sn.velocity[0] ? velocityPoints(sn.velocity[0]) : 0), 0) / snaps.length;
24996
25005
  const recentAvg = avg(recentSnaps);
24997
25006
  const baselineAvg = avg(baselineSnaps);
24998
25007
  const velocityScore = baselineAvg > 0 ? Math.min(100, Math.round(recentAvg / baselineAvg * 100)) : 50;
@@ -29064,7 +29073,7 @@ ${usageLine(decision.usage)}`;
29064
29073
  emitMdAdapterPing(name, { duration_ms: elapsed, success: !isError }, config2.userId, mdProjectSlug);
29065
29074
  }
29066
29075
  const telemetryProjectId = resolveTelemetryProjectId(config2);
29067
- if (telemetryProjectId) {
29076
+ {
29068
29077
  const adapterEmit = adapter2.emitTelemetry?.bind(adapter2) ?? null;
29069
29078
  if (adapterEmit) {
29070
29079
  adapterEmit({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@papi-ai/server",
3
- "version": "0.7.54",
4
- "description": "PAPI MCP server \u2014 AI-powered sprint planning, build execution, and strategy review for software projects",
3
+ "version": "0.7.55",
4
+ "description": "PAPI MCP server AI-powered sprint planning, build execution, and strategy review for software projects",
5
5
  "license": "Elastic-2.0",
6
6
  "mcpName": "io.github.getpapi/papi",
7
7
  "type": "module",