@papi-ai/server 0.7.1 → 0.7.3-alpha.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/index.js +209 -89
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7674,6 +7674,31 @@ ${r.content}` + (r.carry_forward ? `
7674
7674
  `;
7675
7675
  }
7676
7676
  // -------------------------------------------------------------------------
7677
+ // Bug Reports
7678
+ // -------------------------------------------------------------------------
7679
+ async submitBugReport(report) {
7680
+ const [row] = await this.sql`
7681
+ INSERT INTO bug_reports (project_id, user_id, description, diagnostics, status)
7682
+ VALUES (
7683
+ ${report.projectId},
7684
+ ${report.userId ?? null},
7685
+ ${report.description},
7686
+ ${JSON.stringify(report.diagnostics)},
7687
+ ${report.status}
7688
+ )
7689
+ RETURNING id, created_at
7690
+ `;
7691
+ return {
7692
+ id: row.id,
7693
+ projectId: report.projectId,
7694
+ userId: report.userId,
7695
+ description: report.description,
7696
+ diagnostics: report.diagnostics,
7697
+ status: report.status,
7698
+ createdAt: row.created_at
7699
+ };
7700
+ }
7701
+ // -------------------------------------------------------------------------
7677
7702
  // Task Comments (for plan context)
7678
7703
  // -------------------------------------------------------------------------
7679
7704
  async getRecentTaskComments(limit = 20) {
@@ -8488,9 +8513,15 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
8488
8513
  getBuildReportCountForTask(taskId) {
8489
8514
  return this.invoke("getBuildReportCountForTask", [taskId]);
8490
8515
  }
8516
+ // --- Bug Reports ---
8517
+ submitBugReport(report) {
8518
+ return this.invoke("submitBugReport", [report]);
8519
+ }
8491
8520
  // --- Atomic plan write-back ---
8492
- planWriteBack(payload) {
8493
- return this.invoke("planWriteBack", [payload]);
8521
+ async planWriteBack(payload) {
8522
+ const raw = await this.invoke("planWriteBack", [payload]);
8523
+ const map = new Map(Object.entries(raw.newTaskIdMap ?? {}));
8524
+ return { ...raw, newTaskIdMap: map };
8494
8525
  }
8495
8526
  };
8496
8527
  }
@@ -11558,41 +11589,47 @@ async function assembleContext(adapter2, mode, _config, filters, focus) {
11558
11589
  }
11559
11590
  timings["metricsAndReviews"] = t();
11560
11591
  t = startTimer();
11592
+ const leanTargetCycle = health.totalCycles + 1;
11593
+ const [
11594
+ reviewPatternsResult,
11595
+ pendingRecsResult,
11596
+ taskCommentsResult,
11597
+ discoveryCanvasResult,
11598
+ calibrationResult,
11599
+ docsResult,
11600
+ cycleLogResult,
11601
+ preAssignedResult,
11602
+ reportsForCapsResult,
11603
+ contextHashesResult
11604
+ ] = await Promise.allSettled([
11605
+ detectReviewPatterns(reviews2, health.totalCycles, 5),
11606
+ adapter2.getPendingRecommendations(),
11607
+ assembleTaskComments(adapter2),
11608
+ assembleDiscoveryCanvasText(adapter2),
11609
+ Promise.all([
11610
+ adapter2.getEstimationCalibration?.(),
11611
+ adapter2.getModuleEstimationStats?.()
11612
+ ]),
11613
+ adapter2.searchDocs?.({ status: "active", limit: 5 }),
11614
+ adapter2.getCycleLog(5),
11615
+ adapter2.queryBoard({ status: ["Backlog", "In Cycle", "Ready"] }),
11616
+ adapter2.getRecentBuildReports(10),
11617
+ adapter2.getContextHashes?.(health.totalCycles) ?? Promise.resolve(null)
11618
+ ]);
11619
+ timings["parallelReads"] = t();
11561
11620
  let reviewPatternsText2;
11562
- try {
11563
- const reviewPatterns = await detectReviewPatterns(reviews2, health.totalCycles, 5);
11564
- reviewPatternsText2 = hasReviewPatterns(reviewPatterns) ? formatReviewPatterns(reviewPatterns) : void 0;
11565
- } catch {
11621
+ if (reviewPatternsResult.status === "fulfilled") {
11622
+ reviewPatternsText2 = hasReviewPatterns(reviewPatternsResult.value) ? formatReviewPatterns(reviewPatternsResult.value) : void 0;
11566
11623
  }
11567
11624
  let strategyRecommendationsText2;
11568
- try {
11569
- const pendingRecs = await adapter2.getPendingRecommendations();
11570
- if (pendingRecs.length > 0) {
11571
- strategyRecommendationsText2 = formatStrategyRecommendations(pendingRecs, health.totalCycles);
11572
- }
11573
- } catch {
11625
+ if (pendingRecsResult.status === "fulfilled" && pendingRecsResult.value.length > 0) {
11626
+ strategyRecommendationsText2 = formatStrategyRecommendations(pendingRecsResult.value, health.totalCycles);
11574
11627
  }
11575
- timings["advisory"] = t();
11576
- logDataSourceSummary("plan (lean)", [
11577
- { label: "cycleHealth", hasData: !!health },
11578
- { label: "productBrief", hasData: warnIfEmpty("productBrief", productBrief) },
11579
- { label: "activeDecisions", hasData: warnIfEmpty("activeDecisions (lean)", leanSummary.activeDecisions) },
11580
- { label: "buildIntelligence", hasData: warnIfEmpty("buildIntelligence (lean)", leanSummary.buildIntelligence) },
11581
- { label: "cycleLog", hasData: warnIfEmpty("cycleLog (lean)", leanSummary.cycleLog) },
11582
- { label: "board", hasData: warnIfEmpty("board (lean)", leanSummary.board) },
11583
- { label: "metricsSnapshots", hasData: warnIfEmpty("readCycleMetrics", metricsSnapshots2) },
11584
- { label: "reviews", hasData: warnIfEmpty("getRecentReviews", reviews2) }
11585
- ]);
11586
- timings["total"] = totalTimer();
11587
- console.error(`[plan-perf] assembleContext (lean): ${JSON.stringify(timings)}ms`);
11588
- const taskCommentsText = await assembleTaskComments(adapter2);
11589
- const discoveryCanvasText = await assembleDiscoveryCanvasText(adapter2);
11628
+ const taskCommentsText = taskCommentsResult.status === "fulfilled" ? taskCommentsResult.value : void 0;
11629
+ const discoveryCanvasText = discoveryCanvasResult.status === "fulfilled" ? discoveryCanvasResult.value : void 0;
11590
11630
  let estimationCalibrationText;
11591
- try {
11592
- const [calibrationRows, moduleStats] = await Promise.all([
11593
- adapter2.getEstimationCalibration?.(),
11594
- adapter2.getModuleEstimationStats?.()
11595
- ]);
11631
+ if (calibrationResult.status === "fulfilled") {
11632
+ const [calibrationRows, moduleStats] = calibrationResult.value;
11596
11633
  if (calibrationRows && calibrationRows.length > 0) {
11597
11634
  estimationCalibrationText = formatEstimationCalibration(calibrationRows);
11598
11635
  }
@@ -11606,39 +11643,40 @@ async function assembleContext(adapter2, mode, _config, filters, focus) {
11606
11643
  ].join("\n");
11607
11644
  estimationCalibrationText = (estimationCalibrationText ?? "") + moduleLines;
11608
11645
  }
11609
- } catch {
11610
11646
  }
11611
11647
  let registeredDocsText;
11612
- try {
11613
- const docs = await adapter2.searchDocs?.({ status: "active", limit: 5 });
11614
- if (docs && docs.length > 0) {
11615
- const lines = docs.map((d) => `- **${d.title}** (${d.type}) \u2014 ${d.summary}`);
11616
- registeredDocsText = `${docs.length} active research doc(s):
11648
+ if (docsResult.status === "fulfilled" && docsResult.value && docsResult.value.length > 0) {
11649
+ const docs = docsResult.value;
11650
+ const lines = docs.map((d) => `- **${d.title}** (${d.type}) \u2014 ${d.summary}`);
11651
+ registeredDocsText = `${docs.length} active research doc(s):
11617
11652
  ${lines.join("\n")}`;
11618
- }
11619
- } catch {
11620
11653
  }
11621
11654
  const boardFlags = detectBoardFlagsFromText(leanSummary.board);
11622
11655
  let carryForwardStalenessLean;
11623
- try {
11624
- const recentLog = await adapter2.getCycleLog(5);
11625
- carryForwardStalenessLean = computeCarryForwardStaleness(recentLog);
11626
- } catch {
11656
+ if (cycleLogResult.status === "fulfilled") {
11657
+ carryForwardStalenessLean = computeCarryForwardStaleness(cycleLogResult.value);
11627
11658
  }
11628
- const leanTargetCycle = health.totalCycles + 1;
11629
11659
  let preAssignedTextLean;
11630
- try {
11631
- const preAssignedTasks = await adapter2.queryBoard({ status: ["Backlog", "In Cycle", "Ready"] });
11632
- const preAssigned2 = preAssignedTasks.filter((t2) => t2.cycle === leanTargetCycle);
11660
+ if (preAssignedResult.status === "fulfilled") {
11661
+ const preAssigned2 = preAssignedResult.value.filter((t2) => t2.cycle === leanTargetCycle);
11633
11662
  preAssignedTextLean = formatPreAssignedTasks(preAssigned2, leanTargetCycle);
11634
- } catch {
11635
11663
  }
11636
11664
  let recentlyShippedLean;
11637
- try {
11638
- const reportsForCaps = await adapter2.getRecentBuildReports(10);
11639
- recentlyShippedLean = formatRecentlyShippedCapabilities(reportsForCaps);
11640
- } catch {
11665
+ if (reportsForCapsResult.status === "fulfilled") {
11666
+ recentlyShippedLean = formatRecentlyShippedCapabilities(reportsForCapsResult.value);
11641
11667
  }
11668
+ logDataSourceSummary("plan (lean)", [
11669
+ { label: "cycleHealth", hasData: !!health },
11670
+ { label: "productBrief", hasData: warnIfEmpty("productBrief", productBrief) },
11671
+ { label: "activeDecisions", hasData: warnIfEmpty("activeDecisions (lean)", leanSummary.activeDecisions) },
11672
+ { label: "buildIntelligence", hasData: warnIfEmpty("buildIntelligence (lean)", leanSummary.buildIntelligence) },
11673
+ { label: "cycleLog", hasData: warnIfEmpty("cycleLog (lean)", leanSummary.cycleLog) },
11674
+ { label: "board", hasData: warnIfEmpty("board (lean)", leanSummary.board) },
11675
+ { label: "metricsSnapshots", hasData: warnIfEmpty("readCycleMetrics", metricsSnapshots2) },
11676
+ { label: "reviews", hasData: warnIfEmpty("getRecentReviews", reviews2) }
11677
+ ]);
11678
+ timings["total"] = totalTimer();
11679
+ console.error(`[plan-perf] assembleContext (lean): ${JSON.stringify(timings)}ms`);
11642
11680
  let ctx2 = {
11643
11681
  mode,
11644
11682
  cycleNumber: health.totalCycles,
@@ -11663,7 +11701,7 @@ ${lines.join("\n")}`;
11663
11701
  recentlyShippedCapabilities: recentlyShippedLean
11664
11702
  };
11665
11703
  t = startTimer();
11666
- const prevHashes2 = await adapter2.getContextHashes?.(health.totalCycles) ?? null;
11704
+ const prevHashes2 = contextHashesResult.status === "fulfilled" ? contextHashesResult.value : null;
11667
11705
  const { ctx: diffedCtx2, newHashes: newHashes2, savedBytes: savedBytes2 } = applyContextDiff(ctx2, prevHashes2);
11668
11706
  ctx2 = diffedCtx2;
11669
11707
  timings["contextDiff"] = t();
@@ -11686,30 +11724,52 @@ ${lines.join("\n")}`;
11686
11724
  timings["fullQueries"] = t();
11687
11725
  const reports = reportsSinceCycle.length > 0 ? reportsSinceCycle : await adapter2.getRecentBuildReports(5);
11688
11726
  t = startTimer();
11727
+ const [
11728
+ allReportsResult,
11729
+ reviewPatternsResultFull,
11730
+ pendingRecsResultFull,
11731
+ discoveryCanvasResultFull,
11732
+ taskCommentsResultFull,
11733
+ docsResultFull,
11734
+ contextHashesResultFull
11735
+ ] = await Promise.allSettled([
11736
+ adapter2.getRecentBuildReports(50),
11737
+ detectReviewPatterns(reviews, health.totalCycles, 5),
11738
+ adapter2.getPendingRecommendations(),
11739
+ assembleDiscoveryCanvasText(adapter2),
11740
+ assembleTaskComments(adapter2),
11741
+ adapter2.searchDocs?.({ status: "active", limit: 5 }),
11742
+ adapter2.getContextHashes?.(health.totalCycles) ?? Promise.resolve(null)
11743
+ ]);
11744
+ timings["parallelAdvisory"] = t();
11689
11745
  let buildPatternsText;
11690
11746
  let allReportsForPatterns = [];
11691
- try {
11692
- allReportsForPatterns = await adapter2.getRecentBuildReports(50);
11693
- const patterns = await detectBuildPatterns(allReportsForPatterns, health.totalCycles, 5);
11694
- buildPatternsText = hasBuildPatterns(patterns) ? formatBuildPatterns(patterns) : void 0;
11695
- } catch {
11747
+ if (allReportsResult.status === "fulfilled") {
11748
+ allReportsForPatterns = allReportsResult.value;
11749
+ try {
11750
+ const patterns = await detectBuildPatterns(allReportsForPatterns, health.totalCycles, 5);
11751
+ buildPatternsText = hasBuildPatterns(patterns) ? formatBuildPatterns(patterns) : void 0;
11752
+ } catch {
11753
+ }
11696
11754
  }
11697
11755
  let reviewPatternsText;
11698
- try {
11699
- const reviewPatterns = await detectReviewPatterns(reviews, health.totalCycles, 5);
11700
- reviewPatternsText = hasReviewPatterns(reviewPatterns) ? formatReviewPatterns(reviewPatterns) : void 0;
11701
- } catch {
11756
+ if (reviewPatternsResultFull.status === "fulfilled") {
11757
+ reviewPatternsText = hasReviewPatterns(reviewPatternsResultFull.value) ? formatReviewPatterns(reviewPatternsResultFull.value) : void 0;
11702
11758
  }
11703
11759
  let strategyRecommendationsText;
11704
- try {
11705
- const pendingRecs = await adapter2.getPendingRecommendations();
11706
- if (pendingRecs.length > 0) {
11707
- strategyRecommendationsText = formatStrategyRecommendations(pendingRecs);
11708
- }
11709
- } catch {
11760
+ if (pendingRecsResultFull.status === "fulfilled" && pendingRecsResultFull.value.length > 0) {
11761
+ strategyRecommendationsText = formatStrategyRecommendations(pendingRecsResultFull.value);
11710
11762
  }
11711
- timings["advisory"] = t();
11712
11763
  const metricsSnapshots = allReportsForPatterns.length > 0 ? computeSnapshotsFromBuildReports(allReportsForPatterns) : rawMetricsSnapshots.filter((s) => s.accuracy.length > 0 || s.velocity.length > 0);
11764
+ const discoveryCanvasTextFull = discoveryCanvasResultFull.status === "fulfilled" ? discoveryCanvasResultFull.value : void 0;
11765
+ const taskCommentsTextFull = taskCommentsResultFull.status === "fulfilled" ? taskCommentsResultFull.value : void 0;
11766
+ let registeredDocsTextFull;
11767
+ if (docsResultFull.status === "fulfilled" && docsResultFull.value && docsResultFull.value.length > 0) {
11768
+ const docs = docsResultFull.value;
11769
+ const lines = docs.map((d) => `- **${d.title}** (${d.type}) \u2014 ${d.summary}`);
11770
+ registeredDocsTextFull = `${docs.length} active research doc(s):
11771
+ ${lines.join("\n")}`;
11772
+ }
11713
11773
  logDataSourceSummary("plan (full)", [
11714
11774
  { label: "cycleHealth", hasData: !!health },
11715
11775
  { label: "productBrief", hasData: warnIfEmpty("productBrief", productBrief) },
@@ -11723,18 +11783,6 @@ ${lines.join("\n")}`;
11723
11783
  ]);
11724
11784
  timings["total"] = totalTimer();
11725
11785
  console.error(`[plan-perf] assembleContext (full): ${JSON.stringify(timings)}ms`);
11726
- const discoveryCanvasTextFull = await assembleDiscoveryCanvasText(adapter2);
11727
- const taskCommentsTextFull = await assembleTaskComments(adapter2);
11728
- let registeredDocsTextFull;
11729
- try {
11730
- const docs = await adapter2.searchDocs?.({ status: "active", limit: 5 });
11731
- if (docs && docs.length > 0) {
11732
- const lines = docs.map((d) => `- **${d.title}** (${d.type}) \u2014 ${d.summary}`);
11733
- registeredDocsTextFull = `${docs.length} active research doc(s):
11734
- ${lines.join("\n")}`;
11735
- }
11736
- } catch {
11737
- }
11738
11786
  const MAX_PLAN_BUILD_REPORT_CYCLES = 3;
11739
11787
  const cappedReports = reports.length > 0 ? (() => {
11740
11788
  const maxCycle = Math.max(...reports.map((r) => r.cycle ?? 0));
@@ -11773,7 +11821,7 @@ ${lines.join("\n")}`;
11773
11821
  preAssignedTasks: preAssignedText,
11774
11822
  recentlyShippedCapabilities: formatRecentlyShippedCapabilities(allReportsForPatterns)
11775
11823
  };
11776
- const prevHashes = await adapter2.getContextHashes?.(health.totalCycles) ?? null;
11824
+ const prevHashes = contextHashesResultFull.status === "fulfilled" ? contextHashesResultFull.value : null;
11777
11825
  const { ctx: diffedCtx, newHashes, savedBytes } = applyContextDiff(ctx, prevHashes);
11778
11826
  ctx = diffedCtx;
11779
11827
  if (savedBytes > 0) {
@@ -14220,6 +14268,26 @@ ${result.slackWarning}` : "";
14220
14268
  const header = `**Strategy Review \u2014 Cycle ${result.cycleNumber}**
14221
14269
 
14222
14270
  `;
14271
+ let recsCloseOut = "";
14272
+ try {
14273
+ const pendingRecs = await adapter2.getPendingRecommendations();
14274
+ if (pendingRecs.length > 0) {
14275
+ const recLines = pendingRecs.map(
14276
+ (rec) => `- **[${rec.type}]** ${rec.content.slice(0, 120)}${rec.content.length > 120 ? "..." : ""}${rec.target ? ` (target: ${rec.target})` : ""}`
14277
+ );
14278
+ recsCloseOut = `
14279
+
14280
+ ---
14281
+
14282
+ ## Pending Recommendations (${pendingRecs.length})
14283
+
14284
+ ${recLines.join("\n")}
14285
+
14286
+ **Review these recommendations before running \`plan\`.** Use \`idea\` to convert any into backlog tasks, or dismiss by noting which ones don't apply.
14287
+ `;
14288
+ }
14289
+ } catch {
14290
+ }
14223
14291
  const actionReminder = `
14224
14292
 
14225
14293
  ---
@@ -14228,9 +14296,9 @@ ${result.slackWarning}` : "";
14228
14296
  1. Review the AD changes above \u2014 are any resolved/modified ADs still referenced by active tasks?
14229
14297
  2. Act on strategic recommendations \u2014 cancel, defer, or reprioritise tasks as suggested.
14230
14298
  3. Run \`strategy_change\` if any recommendation requires an AD update not already captured.
14231
- 4. Run \`/slack-strategy\` to notify the team.
14299
+ 4. Run \`/patch-notes\` to post curated patch notes to Discord.
14232
14300
  5. Then run \`plan\` for the next cycle.`;
14233
- const response = textResponse(header + result.displayText + slackNote + actionReminder);
14301
+ const response = textResponse(header + result.displayText + slackNote + recsCloseOut + actionReminder);
14234
14302
  return {
14235
14303
  ...response,
14236
14304
  ...contextBytes !== void 0 ? { _contextBytes: contextBytes } : {},
@@ -17666,6 +17734,7 @@ Re-submit with \`notes: "... Reference: <path>"\` to link one, or ignore if none
17666
17734
  }
17667
17735
 
17668
17736
  // src/tools/bug.ts
17737
+ import os2 from "os";
17669
17738
  init_git();
17670
17739
 
17671
17740
  // src/services/bug.ts
@@ -17714,15 +17783,26 @@ async function captureBug(adapter2, input) {
17714
17783
  }
17715
17784
 
17716
17785
  // src/tools/bug.ts
17786
+ function collectDiagnostics(config2) {
17787
+ return {
17788
+ nodeVersion: process.version,
17789
+ platform: os2.platform(),
17790
+ arch: os2.arch(),
17791
+ adapterType: config2.adapterType,
17792
+ projectRoot: config2.projectRoot,
17793
+ hasEndpoint: !!config2.papiEndpoint,
17794
+ lightMode: config2.lightMode
17795
+ };
17796
+ }
17717
17797
  var bugTool = {
17718
17798
  name: "bug",
17719
- description: 'Report a bug as a Backlog task with severity-based priority. Bugs are created with "investigated" maturity so they can be picked up by the next plan without further triage. Does not call the Anthropic API.',
17799
+ description: "Report a bug. Two modes: (1) Default \u2014 creates a Backlog task with severity-based priority for the project board. (2) With report=true \u2014 submits a diagnostic bug report with system info for cross-project visibility (external user issue reporting). Does not call the Anthropic API.",
17720
17800
  inputSchema: {
17721
17801
  type: "object",
17722
17802
  properties: {
17723
17803
  text: {
17724
17804
  type: "string",
17725
- description: "The bug description \u2014 becomes the task title."
17805
+ description: "The bug description \u2014 becomes the task title (default mode) or report description (report mode)."
17726
17806
  },
17727
17807
  severity: {
17728
17808
  type: "string",
@@ -17744,6 +17824,10 @@ var bugTool = {
17744
17824
  phase: {
17745
17825
  type: "string",
17746
17826
  description: "Target phase (default: current active phase)."
17827
+ },
17828
+ report: {
17829
+ type: "boolean",
17830
+ description: "When true, submits a diagnostic bug report with system info instead of creating a board task. Use this to report issues to PAPI maintainers."
17747
17831
  }
17748
17832
  },
17749
17833
  required: ["text"]
@@ -17754,6 +17838,38 @@ async function handleBug(adapter2, config2, args) {
17754
17838
  if (!text) {
17755
17839
  return errorResponse("text is required \u2014 describe the bug you want to report.");
17756
17840
  }
17841
+ if (args.report === true) {
17842
+ if (!adapter2.submitBugReport) {
17843
+ return errorResponse("Bug reports require a database adapter (pg or proxy). The md adapter does not support bug reports.");
17844
+ }
17845
+ const diagnostics = collectDiagnostics(config2);
17846
+ const notes = args.notes?.trim();
17847
+ if (notes) {
17848
+ diagnostics.userNotes = notes;
17849
+ }
17850
+ let projectId;
17851
+ try {
17852
+ const health = await adapter2.getCycleHealth();
17853
+ projectId = adapter2.projectId ?? "unknown";
17854
+ diagnostics.cycleNumber = health.totalCycles;
17855
+ } catch {
17856
+ projectId = "unknown";
17857
+ }
17858
+ const report = await adapter2.submitBugReport({
17859
+ projectId,
17860
+ description: text,
17861
+ diagnostics,
17862
+ status: "open"
17863
+ });
17864
+ return textResponse(
17865
+ `**Bug report submitted** \u2014 ID: \`${report.id}\`
17866
+
17867
+ Description: ${text}
17868
+ Diagnostics collected: Node ${diagnostics.nodeVersion}, ${diagnostics.platform}/${diagnostics.arch}, adapter=${diagnostics.adapterType}
17869
+
17870
+ This report is visible to PAPI maintainers. No further action needed from you.`
17871
+ );
17872
+ }
17757
17873
  const MAX_NOTES_LENGTH = 2e3;
17758
17874
  let rawNotes = args.notes?.trim();
17759
17875
  let notesTruncated = false;
@@ -17808,10 +17924,14 @@ async function recordAdHoc(adapter2, input) {
17808
17924
  if (!existing) {
17809
17925
  throw new Error(`Task "${input.taskId}" not found on the board. Check the task ID and try again.`);
17810
17926
  }
17811
- await adapter2.updateTask(input.taskId, {
17927
+ const updatePayload = {
17812
17928
  notes: existing.notes ? `${existing.notes}
17813
17929
  [ad-hoc] ${input.notes || "Work recorded via ad_hoc"}` : `[ad-hoc] ${input.notes || "Work recorded via ad_hoc"}`
17814
- });
17930
+ };
17931
+ if (existing.status === "In Progress") {
17932
+ updatePayload.status = "In Review";
17933
+ }
17934
+ await adapter2.updateTask(input.taskId, updatePayload);
17815
17935
  task = await adapter2.getTask(input.taskId);
17816
17936
  } else {
17817
17937
  const phase = resolveCurrentPhase(phases);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papi-ai/server",
3
- "version": "0.7.1",
3
+ "version": "0.7.3-alpha.1",
4
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
  "type": "module",