@papi-ai/server 0.7.56 → 0.7.57

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/index.js CHANGED
@@ -8425,6 +8425,21 @@ function computeCycleEffort(cycleTaskRows, cycleReports) {
8425
8425
  function velocityPoints(v) {
8426
8426
  return v.deliveredPoints ?? v.effortPoints;
8427
8427
  }
8428
+ function resolveCompletedCycleDates(existingStart, earliestActivity, now) {
8429
+ const startDate = existingStart || earliestActivity || now;
8430
+ const startMs = Date.parse(startDate);
8431
+ const endMs = Date.parse(now);
8432
+ const known = Number.isFinite(startMs) && Number.isFinite(endMs) && startMs < endMs;
8433
+ return { startDate, endDate: known ? now : null };
8434
+ }
8435
+ function earliestCycleActivity(reports) {
8436
+ let earliest;
8437
+ for (const r of reports) {
8438
+ const ts = r.startedAt || r.createdAt || r.date;
8439
+ if (ts && (!earliest || Date.parse(ts) < Date.parse(earliest))) earliest = ts;
8440
+ }
8441
+ return earliest;
8442
+ }
8428
8443
  function computeSnapshotsFromBuildReports(reports, tasks) {
8429
8444
  const reportsByCycle = /* @__PURE__ */ new Map();
8430
8445
  for (const r of reports) {
@@ -8826,7 +8841,7 @@ PRE-BUILD VERIFICATION
8826
8841
  [List 2-5 specific file paths the builder should read BEFORE implementing to check if the functionality already exists. Derive these from FILES LIKELY TOUCHED \u2014 pick the files most likely to already contain the target functionality. ALSO mandate a docs sweep, not just file-existence (task-2161): name any docs the builder should check via doc_search or the docs index \u2014 a design/research/status:final doc or a prior task may already cover the work. If >80% of the scope is already implemented, the builder should report "already built" instead of re-implementing. Include this section for EVERY task \u2014 it prevents wasted build slots on already-shipped code.]
8827
8842
 
8828
8843
  FILES LIKELY TOUCHED
8829
- [files]
8844
+ [files \u2014 real paths only. TEST FILE LOCATIONS (task-2606): PAPI tests are NOT co-located next to source in a src/**/__tests__/ folder. Use the actual layout: server tests \u2192 packages/server/tests/<name>.test.ts; adapter-pg tests \u2192 packages/adapter-pg/src/__tests__/<name>.test.ts; dashboard/root tests \u2192 tests/<area>/<name>.test.ts. Do NOT invent packages/server/src/**/__tests__/ paths \u2014 they do not exist.]
8830
8845
 
8831
8846
  EFFORT
8832
8847
  [XS/S/M/L/XL]
@@ -9983,7 +9998,7 @@ REFERENCE DOCS
9983
9998
  [Optional \u2014 paths to docs/ files with background context. Omit if not needed.]
9984
9999
 
9985
10000
  FILES LIKELY TOUCHED
9986
- [files]
10001
+ [files \u2014 real paths only. TEST FILE LOCATIONS (task-2606): PAPI tests are NOT co-located next to source in a src/**/__tests__/ folder. Use the actual layout: server tests \u2192 packages/server/tests/<name>.test.ts; adapter-pg tests \u2192 packages/adapter-pg/src/__tests__/<name>.test.ts; dashboard/root tests \u2192 tests/<area>/<name>.test.ts. Do NOT invent packages/server/src/**/__tests__/ paths \u2014 they do not exist.]
9987
10002
 
9988
10003
  EFFORT
9989
10004
  [XS/S/M/L/XL]`;
@@ -19056,20 +19071,11 @@ To override, pass force=true (emits a telemetry warning).`
19056
19071
  existing = (await adapter2.readCycles()).find((c) => c.number === currentCycle);
19057
19072
  } catch {
19058
19073
  }
19059
- const completedCycle = {
19060
- id: `cycle-${currentCycle}`,
19061
- number: currentCycle,
19062
- status: "complete",
19063
- startDate: existing?.startDate ?? (/* @__PURE__ */ new Date()).toISOString(),
19064
- endDate: (/* @__PURE__ */ new Date()).toISOString(),
19065
- goals: existing?.goals ?? [],
19066
- boardHealth: existing?.boardHealth ?? "",
19067
- taskIds: existing?.taskIds ?? []
19068
- };
19069
19074
  let snapshot;
19075
+ let cycleReports = [];
19070
19076
  if (adapter2.getBuildReportsSince) {
19071
19077
  try {
19072
- const cycleReports = (await adapter2.getBuildReportsSince(currentCycle)).filter((r) => r.cycle === currentCycle);
19078
+ cycleReports = (await adapter2.getBuildReportsSince(currentCycle)).filter((r) => r.cycle === currentCycle);
19073
19079
  const cycleTasks = (await adapter2.queryBoard({ cycleSince: currentCycle, compact: true })).filter((t) => t.cycle === currentCycle);
19074
19080
  [snapshot] = computeSnapshotsFromBuildReports(cycleReports, cycleTasks);
19075
19081
  } catch (err) {
@@ -19078,6 +19084,22 @@ To override, pass force=true (emits a telemetry warning).`
19078
19084
  );
19079
19085
  }
19080
19086
  }
19087
+ const releaseNow = (/* @__PURE__ */ new Date()).toISOString();
19088
+ const { startDate: resolvedStart, endDate: resolvedEnd } = resolveCompletedCycleDates(
19089
+ existing?.startDate,
19090
+ earliestCycleActivity(cycleReports),
19091
+ releaseNow
19092
+ );
19093
+ const completedCycle = {
19094
+ id: `cycle-${currentCycle}`,
19095
+ number: currentCycle,
19096
+ status: "complete",
19097
+ startDate: resolvedStart,
19098
+ endDate: resolvedEnd ?? void 0,
19099
+ goals: existing?.goals ?? [],
19100
+ boardHealth: existing?.boardHealth ?? "",
19101
+ taskIds: existing?.taskIds ?? []
19102
+ };
19081
19103
  if (typeof adapter2.commitRelease === "function") {
19082
19104
  try {
19083
19105
  await adapter2.commitRelease({ cycle: completedCycle, snapshot: snapshot ?? null });
@@ -25782,6 +25804,50 @@ function countStalledP1(warnings) {
25782
25804
  const matches = stallLine.match(/task-\d+/g);
25783
25805
  return matches ? matches.length : 0;
25784
25806
  }
25807
+ function truncateTaskTitle(title, max = 55) {
25808
+ const t = (title ?? "").trim();
25809
+ if (!t) return "untitled";
25810
+ if (t.length <= max) return t;
25811
+ return t.slice(0, max - 1).trimEnd() + "\u2026";
25812
+ }
25813
+ async function resolveCarryForwardRefs(carryForward, tasks, adapter2) {
25814
+ const refs = /* @__PURE__ */ new Map();
25815
+ const ids = carryForward?.match(/\btask-\d+\b/g);
25816
+ if (!ids) return refs;
25817
+ const unique = [...new Set(ids)];
25818
+ const byId = new Map(tasks.filter((t) => t.displayId).map((t) => [t.displayId, t]));
25819
+ const missing = [];
25820
+ for (const id of unique) {
25821
+ const t = byId.get(id);
25822
+ if (t) refs.set(id, { title: t.title, status: t.status });
25823
+ else missing.push(id);
25824
+ }
25825
+ if (missing.length > 0 && adapter2?.getTasks) {
25826
+ try {
25827
+ const fetched = await adapter2.getTasks(missing);
25828
+ for (const t of fetched) {
25829
+ if (t.displayId) refs.set(t.displayId, { title: t.title, status: t.status });
25830
+ }
25831
+ } catch {
25832
+ }
25833
+ }
25834
+ return refs;
25835
+ }
25836
+ function annotateTaskRefs(text, refs) {
25837
+ if (!text || refs.size === 0) return text;
25838
+ const RESOLVED = /* @__PURE__ */ new Set(["Done", "Cancelled"]);
25839
+ return text.replace(/\btask-\d+\b(?!\s*[(—–-])/g, (id, offset, full) => {
25840
+ const ref = refs.get(id);
25841
+ if (!ref) return id;
25842
+ const rest = full.slice(offset + id.length);
25843
+ if (/^\s*[(—–-]/.test(rest)) return id;
25844
+ const resolved = ref.status && RESOLVED.has(ref.status);
25845
+ const titlePart = ref.title ? ` \u2014 ${truncateTaskTitle(ref.title)}` : "";
25846
+ const statusPart = resolved ? ` [\u2713 ${ref.status}]` : "";
25847
+ if (!titlePart && !statusPart) return id;
25848
+ return `${id}${titlePart}${statusPart}`;
25849
+ });
25850
+ }
25785
25851
  function formatSynthesisParagraph(opts) {
25786
25852
  const parts = [];
25787
25853
  parts.push(
@@ -25801,7 +25867,7 @@ function formatSynthesisParagraph(opts) {
25801
25867
  parts.push(`Next: ${cleanMode}`);
25802
25868
  return parts.join(" ");
25803
25869
  }
25804
- function formatOrientSummary(health, buildInfo, hierarchy, latestTag, projectRoot, environment = "unknown", subAgents = [], projectName, teamSummary, clientName) {
25870
+ function formatOrientSummary(health, buildInfo, hierarchy, latestTag, projectRoot, environment = "unknown", subAgents = [], projectName, teamSummary, clientName, taskRefs = /* @__PURE__ */ new Map()) {
25805
25871
  const lines = [];
25806
25872
  const cycleIsComplete = health.latestCycleStatus === "complete";
25807
25873
  const tagSuffix = latestTag ? ` \u2014 ${latestTag}` : "";
@@ -25944,7 +26010,7 @@ function formatOrientSummary(health, buildInfo, hierarchy, latestTag, projectRoo
25944
26010
  const hasCarryForward = health.carryForward !== "None found" && !health.carryForward.startsWith("No carry-forward");
25945
26011
  if (hasCarryForward) {
25946
26012
  lines.push("## Carry-Forward");
25947
- lines.push(health.carryForward);
26013
+ lines.push(annotateTaskRefs(health.carryForward, taskRefs));
25948
26014
  lines.push("");
25949
26015
  }
25950
26016
  const hasMetrics = health.metricsSection !== "Could not read methodology metrics." && !health.metricsSection.includes("undefined");
@@ -26233,7 +26299,7 @@ async function handleOrient(adapter2, config2, args = {}, clientName) {
26233
26299
  (t) => t.createdCycle != null && currentCycle - t.createdCycle >= 3
26234
26300
  );
26235
26301
  if (stalledP1.length === 0) return void 0;
26236
- const ids = stalledP1.map((t) => `${t.displayId} (${currentCycle - (t.createdCycle ?? currentCycle)}+ cycles)`).join(", ");
26302
+ const ids = stalledP1.map((t) => `${t.displayId} \u2014 ${truncateTaskTitle(t.title)} (${currentCycle - (t.createdCycle ?? currentCycle)}+ cycles)`).join(", ");
26237
26303
  return `\u26A0\uFE0F P1 tasks stalled 3+ cycles: ${ids}`;
26238
26304
  }),
26239
26305
  // task-1917 (C280): Owner Action Queue surface. Counts the user's open
@@ -26251,10 +26317,12 @@ async function handleOrient(adapter2, config2, args = {}, clientName) {
26251
26317
  let dueSuffix = "";
26252
26318
  if (adapter2.countDueOwnerActions) {
26253
26319
  const due = await adapter2.countDueOwnerActions(userId, projectId);
26254
- const parts = [];
26255
- if (due.dueToday > 0) parts.push(`${due.dueToday} due today`);
26256
- if (due.overdue > 0) parts.push(`${due.overdue} overdue`);
26257
- if (parts.length > 0) dueSuffix = ` (${parts.join(", ")})`;
26320
+ if (due.urgent > 0) {
26321
+ const detail = [];
26322
+ if (due.overdue > 0) detail.push(`${due.overdue} overdue`);
26323
+ if (due.dueToday > 0) detail.push(`${due.dueToday} due today`);
26324
+ dueSuffix = detail.length > 0 ? ` (${due.urgent} need you now \u2014 ${detail.join(", ")})` : ` (${due.urgent} need you now)`;
26325
+ }
26258
26326
  }
26259
26327
  return `\u26A0\uFE0F ${count} action${count === 1 ? "" : "s"} waiting on you${dueSuffix} \u2014 open /hub to triage the Owner Action Queue.`;
26260
26328
  } catch {
@@ -26657,7 +26725,8 @@ ${section}`;
26657
26725
  ]);
26658
26726
  const teamSummary = [teamSummaryLine, releaseHistoryLine].filter(Boolean).join("\n") || void 0;
26659
26727
  const deepHint = deepHousekeeping ? "" : "\n\n*Tip: pass `full: true` for Research Signals + version-drift, or `deep_housekeeping: true` to also check orphaned branches, merged-but-In-Progress tasks, unrecorded commits, unregistered docs, and stale skill forks (implies `full`).*";
26660
- return textResponse(projectBannerNote + formatOrientSummary(healthResult, buildInfo, hierarchy, latestTag, config2.projectRoot, environment, subAgents, projectName, teamSummary, clientName) + unblockNote + deferredGateNote + feedbackResolvedNote + alertsNote + ttfvNote + reconciliationNote + mergedInProgressNote + unrecordedNote + unregisteredDocsNote + staleSkillsNote + researchSignalsNote + recsNote + pendingReviewNote + patternsNote + unactionedIssuesNote + skillProposalsNote + sessionGuidanceNote + versionNote + enrichmentNote + deliveryShapeNote + preBuildCheckNote + deepHint + enrichmentFilesSection);
26728
+ const carryForwardRefs = await resolveCarryForwardRefs(healthResult.carryForward, allTasks, adapter2);
26729
+ return textResponse(projectBannerNote + formatOrientSummary(healthResult, buildInfo, hierarchy, latestTag, config2.projectRoot, environment, subAgents, projectName, teamSummary, clientName, carryForwardRefs) + unblockNote + deferredGateNote + feedbackResolvedNote + alertsNote + ttfvNote + reconciliationNote + mergedInProgressNote + unrecordedNote + unregisteredDocsNote + staleSkillsNote + researchSignalsNote + recsNote + pendingReviewNote + patternsNote + unactionedIssuesNote + skillProposalsNote + sessionGuidanceNote + versionNote + enrichmentNote + deliveryShapeNote + preBuildCheckNote + deepHint + enrichmentFilesSection);
26661
26730
  } catch (err) {
26662
26731
  const message = err instanceof Error ? err.message : String(err);
26663
26732
  const isKnownFriendly = /^(Orient failed|Project not found|No project|Setup required)/i.test(message);
package/dist/prompts.js CHANGED
@@ -69,7 +69,7 @@ PRE-BUILD VERIFICATION
69
69
  [List 2-5 specific file paths the builder should read BEFORE implementing to check if the functionality already exists. Derive these from FILES LIKELY TOUCHED \u2014 pick the files most likely to already contain the target functionality. ALSO mandate a docs sweep, not just file-existence (task-2161): name any docs the builder should check via doc_search or the docs index \u2014 a design/research/status:final doc or a prior task may already cover the work. If >80% of the scope is already implemented, the builder should report "already built" instead of re-implementing. Include this section for EVERY task \u2014 it prevents wasted build slots on already-shipped code.]
70
70
 
71
71
  FILES LIKELY TOUCHED
72
- [files]
72
+ [files \u2014 real paths only. TEST FILE LOCATIONS (task-2606): PAPI tests are NOT co-located next to source in a src/**/__tests__/ folder. Use the actual layout: server tests \u2192 packages/server/tests/<name>.test.ts; adapter-pg tests \u2192 packages/adapter-pg/src/__tests__/<name>.test.ts; dashboard/root tests \u2192 tests/<area>/<name>.test.ts. Do NOT invent packages/server/src/**/__tests__/ paths \u2014 they do not exist.]
73
73
 
74
74
  EFFORT
75
75
  [XS/S/M/L/XL]
@@ -1226,7 +1226,7 @@ REFERENCE DOCS
1226
1226
  [Optional \u2014 paths to docs/ files with background context. Omit if not needed.]
1227
1227
 
1228
1228
  FILES LIKELY TOUCHED
1229
- [files]
1229
+ [files \u2014 real paths only. TEST FILE LOCATIONS (task-2606): PAPI tests are NOT co-located next to source in a src/**/__tests__/ folder. Use the actual layout: server tests \u2192 packages/server/tests/<name>.test.ts; adapter-pg tests \u2192 packages/adapter-pg/src/__tests__/<name>.test.ts; dashboard/root tests \u2192 tests/<area>/<name>.test.ts. Do NOT invent packages/server/src/**/__tests__/ paths \u2014 they do not exist.]
1230
1230
 
1231
1231
  EFFORT
1232
1232
  [XS/S/M/L/XL]`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@papi-ai/server",
3
- "version": "0.7.56",
4
- "description": "PAPI MCP server AI-powered sprint planning, build execution, and strategy review for software projects",
3
+ "version": "0.7.57",
4
+ "description": "PAPI MCP server \u2014 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",