@papi-ai/server 0.7.1-alpha.1 → 0.7.2

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 +206 -88
  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,6 +8513,10 @@ 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
8521
  planWriteBack(payload) {
8493
8522
  return this.invoke("planWriteBack", [payload]);
@@ -11558,41 +11587,47 @@ async function assembleContext(adapter2, mode, _config, filters, focus) {
11558
11587
  }
11559
11588
  timings["metricsAndReviews"] = t();
11560
11589
  t = startTimer();
11590
+ const leanTargetCycle = health.totalCycles + 1;
11591
+ const [
11592
+ reviewPatternsResult,
11593
+ pendingRecsResult,
11594
+ taskCommentsResult,
11595
+ discoveryCanvasResult,
11596
+ calibrationResult,
11597
+ docsResult,
11598
+ cycleLogResult,
11599
+ preAssignedResult,
11600
+ reportsForCapsResult,
11601
+ contextHashesResult
11602
+ ] = await Promise.allSettled([
11603
+ detectReviewPatterns(reviews2, health.totalCycles, 5),
11604
+ adapter2.getPendingRecommendations(),
11605
+ assembleTaskComments(adapter2),
11606
+ assembleDiscoveryCanvasText(adapter2),
11607
+ Promise.all([
11608
+ adapter2.getEstimationCalibration?.(),
11609
+ adapter2.getModuleEstimationStats?.()
11610
+ ]),
11611
+ adapter2.searchDocs?.({ status: "active", limit: 5 }),
11612
+ adapter2.getCycleLog(5),
11613
+ adapter2.queryBoard({ status: ["Backlog", "In Cycle", "Ready"] }),
11614
+ adapter2.getRecentBuildReports(10),
11615
+ adapter2.getContextHashes?.(health.totalCycles) ?? Promise.resolve(null)
11616
+ ]);
11617
+ timings["parallelReads"] = t();
11561
11618
  let reviewPatternsText2;
11562
- try {
11563
- const reviewPatterns = await detectReviewPatterns(reviews2, health.totalCycles, 5);
11564
- reviewPatternsText2 = hasReviewPatterns(reviewPatterns) ? formatReviewPatterns(reviewPatterns) : void 0;
11565
- } catch {
11619
+ if (reviewPatternsResult.status === "fulfilled") {
11620
+ reviewPatternsText2 = hasReviewPatterns(reviewPatternsResult.value) ? formatReviewPatterns(reviewPatternsResult.value) : void 0;
11566
11621
  }
11567
11622
  let strategyRecommendationsText2;
11568
- try {
11569
- const pendingRecs = await adapter2.getPendingRecommendations();
11570
- if (pendingRecs.length > 0) {
11571
- strategyRecommendationsText2 = formatStrategyRecommendations(pendingRecs, health.totalCycles);
11572
- }
11573
- } catch {
11623
+ if (pendingRecsResult.status === "fulfilled" && pendingRecsResult.value.length > 0) {
11624
+ strategyRecommendationsText2 = formatStrategyRecommendations(pendingRecsResult.value, health.totalCycles);
11574
11625
  }
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);
11626
+ const taskCommentsText = taskCommentsResult.status === "fulfilled" ? taskCommentsResult.value : void 0;
11627
+ const discoveryCanvasText = discoveryCanvasResult.status === "fulfilled" ? discoveryCanvasResult.value : void 0;
11590
11628
  let estimationCalibrationText;
11591
- try {
11592
- const [calibrationRows, moduleStats] = await Promise.all([
11593
- adapter2.getEstimationCalibration?.(),
11594
- adapter2.getModuleEstimationStats?.()
11595
- ]);
11629
+ if (calibrationResult.status === "fulfilled") {
11630
+ const [calibrationRows, moduleStats] = calibrationResult.value;
11596
11631
  if (calibrationRows && calibrationRows.length > 0) {
11597
11632
  estimationCalibrationText = formatEstimationCalibration(calibrationRows);
11598
11633
  }
@@ -11606,39 +11641,40 @@ async function assembleContext(adapter2, mode, _config, filters, focus) {
11606
11641
  ].join("\n");
11607
11642
  estimationCalibrationText = (estimationCalibrationText ?? "") + moduleLines;
11608
11643
  }
11609
- } catch {
11610
11644
  }
11611
11645
  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):
11646
+ if (docsResult.status === "fulfilled" && docsResult.value && docsResult.value.length > 0) {
11647
+ const docs = docsResult.value;
11648
+ const lines = docs.map((d) => `- **${d.title}** (${d.type}) \u2014 ${d.summary}`);
11649
+ registeredDocsText = `${docs.length} active research doc(s):
11617
11650
  ${lines.join("\n")}`;
11618
- }
11619
- } catch {
11620
11651
  }
11621
11652
  const boardFlags = detectBoardFlagsFromText(leanSummary.board);
11622
11653
  let carryForwardStalenessLean;
11623
- try {
11624
- const recentLog = await adapter2.getCycleLog(5);
11625
- carryForwardStalenessLean = computeCarryForwardStaleness(recentLog);
11626
- } catch {
11654
+ if (cycleLogResult.status === "fulfilled") {
11655
+ carryForwardStalenessLean = computeCarryForwardStaleness(cycleLogResult.value);
11627
11656
  }
11628
- const leanTargetCycle = health.totalCycles + 1;
11629
11657
  let preAssignedTextLean;
11630
- try {
11631
- const preAssignedTasks = await adapter2.queryBoard({ status: ["Backlog", "In Cycle", "Ready"] });
11632
- const preAssigned2 = preAssignedTasks.filter((t2) => t2.cycle === leanTargetCycle);
11658
+ if (preAssignedResult.status === "fulfilled") {
11659
+ const preAssigned2 = preAssignedResult.value.filter((t2) => t2.cycle === leanTargetCycle);
11633
11660
  preAssignedTextLean = formatPreAssignedTasks(preAssigned2, leanTargetCycle);
11634
- } catch {
11635
11661
  }
11636
11662
  let recentlyShippedLean;
11637
- try {
11638
- const reportsForCaps = await adapter2.getRecentBuildReports(10);
11639
- recentlyShippedLean = formatRecentlyShippedCapabilities(reportsForCaps);
11640
- } catch {
11663
+ if (reportsForCapsResult.status === "fulfilled") {
11664
+ recentlyShippedLean = formatRecentlyShippedCapabilities(reportsForCapsResult.value);
11641
11665
  }
11666
+ logDataSourceSummary("plan (lean)", [
11667
+ { label: "cycleHealth", hasData: !!health },
11668
+ { label: "productBrief", hasData: warnIfEmpty("productBrief", productBrief) },
11669
+ { label: "activeDecisions", hasData: warnIfEmpty("activeDecisions (lean)", leanSummary.activeDecisions) },
11670
+ { label: "buildIntelligence", hasData: warnIfEmpty("buildIntelligence (lean)", leanSummary.buildIntelligence) },
11671
+ { label: "cycleLog", hasData: warnIfEmpty("cycleLog (lean)", leanSummary.cycleLog) },
11672
+ { label: "board", hasData: warnIfEmpty("board (lean)", leanSummary.board) },
11673
+ { label: "metricsSnapshots", hasData: warnIfEmpty("readCycleMetrics", metricsSnapshots2) },
11674
+ { label: "reviews", hasData: warnIfEmpty("getRecentReviews", reviews2) }
11675
+ ]);
11676
+ timings["total"] = totalTimer();
11677
+ console.error(`[plan-perf] assembleContext (lean): ${JSON.stringify(timings)}ms`);
11642
11678
  let ctx2 = {
11643
11679
  mode,
11644
11680
  cycleNumber: health.totalCycles,
@@ -11663,7 +11699,7 @@ ${lines.join("\n")}`;
11663
11699
  recentlyShippedCapabilities: recentlyShippedLean
11664
11700
  };
11665
11701
  t = startTimer();
11666
- const prevHashes2 = await adapter2.getContextHashes?.(health.totalCycles) ?? null;
11702
+ const prevHashes2 = contextHashesResult.status === "fulfilled" ? contextHashesResult.value : null;
11667
11703
  const { ctx: diffedCtx2, newHashes: newHashes2, savedBytes: savedBytes2 } = applyContextDiff(ctx2, prevHashes2);
11668
11704
  ctx2 = diffedCtx2;
11669
11705
  timings["contextDiff"] = t();
@@ -11686,30 +11722,52 @@ ${lines.join("\n")}`;
11686
11722
  timings["fullQueries"] = t();
11687
11723
  const reports = reportsSinceCycle.length > 0 ? reportsSinceCycle : await adapter2.getRecentBuildReports(5);
11688
11724
  t = startTimer();
11725
+ const [
11726
+ allReportsResult,
11727
+ reviewPatternsResultFull,
11728
+ pendingRecsResultFull,
11729
+ discoveryCanvasResultFull,
11730
+ taskCommentsResultFull,
11731
+ docsResultFull,
11732
+ contextHashesResultFull
11733
+ ] = await Promise.allSettled([
11734
+ adapter2.getRecentBuildReports(50),
11735
+ detectReviewPatterns(reviews, health.totalCycles, 5),
11736
+ adapter2.getPendingRecommendations(),
11737
+ assembleDiscoveryCanvasText(adapter2),
11738
+ assembleTaskComments(adapter2),
11739
+ adapter2.searchDocs?.({ status: "active", limit: 5 }),
11740
+ adapter2.getContextHashes?.(health.totalCycles) ?? Promise.resolve(null)
11741
+ ]);
11742
+ timings["parallelAdvisory"] = t();
11689
11743
  let buildPatternsText;
11690
11744
  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 {
11745
+ if (allReportsResult.status === "fulfilled") {
11746
+ allReportsForPatterns = allReportsResult.value;
11747
+ try {
11748
+ const patterns = await detectBuildPatterns(allReportsForPatterns, health.totalCycles, 5);
11749
+ buildPatternsText = hasBuildPatterns(patterns) ? formatBuildPatterns(patterns) : void 0;
11750
+ } catch {
11751
+ }
11696
11752
  }
11697
11753
  let reviewPatternsText;
11698
- try {
11699
- const reviewPatterns = await detectReviewPatterns(reviews, health.totalCycles, 5);
11700
- reviewPatternsText = hasReviewPatterns(reviewPatterns) ? formatReviewPatterns(reviewPatterns) : void 0;
11701
- } catch {
11754
+ if (reviewPatternsResultFull.status === "fulfilled") {
11755
+ reviewPatternsText = hasReviewPatterns(reviewPatternsResultFull.value) ? formatReviewPatterns(reviewPatternsResultFull.value) : void 0;
11702
11756
  }
11703
11757
  let strategyRecommendationsText;
11704
- try {
11705
- const pendingRecs = await adapter2.getPendingRecommendations();
11706
- if (pendingRecs.length > 0) {
11707
- strategyRecommendationsText = formatStrategyRecommendations(pendingRecs);
11708
- }
11709
- } catch {
11758
+ if (pendingRecsResultFull.status === "fulfilled" && pendingRecsResultFull.value.length > 0) {
11759
+ strategyRecommendationsText = formatStrategyRecommendations(pendingRecsResultFull.value);
11710
11760
  }
11711
- timings["advisory"] = t();
11712
11761
  const metricsSnapshots = allReportsForPatterns.length > 0 ? computeSnapshotsFromBuildReports(allReportsForPatterns) : rawMetricsSnapshots.filter((s) => s.accuracy.length > 0 || s.velocity.length > 0);
11762
+ const discoveryCanvasTextFull = discoveryCanvasResultFull.status === "fulfilled" ? discoveryCanvasResultFull.value : void 0;
11763
+ const taskCommentsTextFull = taskCommentsResultFull.status === "fulfilled" ? taskCommentsResultFull.value : void 0;
11764
+ let registeredDocsTextFull;
11765
+ if (docsResultFull.status === "fulfilled" && docsResultFull.value && docsResultFull.value.length > 0) {
11766
+ const docs = docsResultFull.value;
11767
+ const lines = docs.map((d) => `- **${d.title}** (${d.type}) \u2014 ${d.summary}`);
11768
+ registeredDocsTextFull = `${docs.length} active research doc(s):
11769
+ ${lines.join("\n")}`;
11770
+ }
11713
11771
  logDataSourceSummary("plan (full)", [
11714
11772
  { label: "cycleHealth", hasData: !!health },
11715
11773
  { label: "productBrief", hasData: warnIfEmpty("productBrief", productBrief) },
@@ -11723,18 +11781,6 @@ ${lines.join("\n")}`;
11723
11781
  ]);
11724
11782
  timings["total"] = totalTimer();
11725
11783
  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
11784
  const MAX_PLAN_BUILD_REPORT_CYCLES = 3;
11739
11785
  const cappedReports = reports.length > 0 ? (() => {
11740
11786
  const maxCycle = Math.max(...reports.map((r) => r.cycle ?? 0));
@@ -11773,7 +11819,7 @@ ${lines.join("\n")}`;
11773
11819
  preAssignedTasks: preAssignedText,
11774
11820
  recentlyShippedCapabilities: formatRecentlyShippedCapabilities(allReportsForPatterns)
11775
11821
  };
11776
- const prevHashes = await adapter2.getContextHashes?.(health.totalCycles) ?? null;
11822
+ const prevHashes = contextHashesResultFull.status === "fulfilled" ? contextHashesResultFull.value : null;
11777
11823
  const { ctx: diffedCtx, newHashes, savedBytes } = applyContextDiff(ctx, prevHashes);
11778
11824
  ctx = diffedCtx;
11779
11825
  if (savedBytes > 0) {
@@ -14220,6 +14266,26 @@ ${result.slackWarning}` : "";
14220
14266
  const header = `**Strategy Review \u2014 Cycle ${result.cycleNumber}**
14221
14267
 
14222
14268
  `;
14269
+ let recsCloseOut = "";
14270
+ try {
14271
+ const pendingRecs = await adapter2.getPendingRecommendations();
14272
+ if (pendingRecs.length > 0) {
14273
+ const recLines = pendingRecs.map(
14274
+ (rec) => `- **[${rec.type}]** ${rec.content.slice(0, 120)}${rec.content.length > 120 ? "..." : ""}${rec.target ? ` (target: ${rec.target})` : ""}`
14275
+ );
14276
+ recsCloseOut = `
14277
+
14278
+ ---
14279
+
14280
+ ## Pending Recommendations (${pendingRecs.length})
14281
+
14282
+ ${recLines.join("\n")}
14283
+
14284
+ **Review these recommendations before running \`plan\`.** Use \`idea\` to convert any into backlog tasks, or dismiss by noting which ones don't apply.
14285
+ `;
14286
+ }
14287
+ } catch {
14288
+ }
14223
14289
  const actionReminder = `
14224
14290
 
14225
14291
  ---
@@ -14228,9 +14294,9 @@ ${result.slackWarning}` : "";
14228
14294
  1. Review the AD changes above \u2014 are any resolved/modified ADs still referenced by active tasks?
14229
14295
  2. Act on strategic recommendations \u2014 cancel, defer, or reprioritise tasks as suggested.
14230
14296
  3. Run \`strategy_change\` if any recommendation requires an AD update not already captured.
14231
- 4. Run \`/slack-strategy\` to notify the team.
14297
+ 4. Run \`/patch-notes\` to post curated patch notes to Discord.
14232
14298
  5. Then run \`plan\` for the next cycle.`;
14233
- const response = textResponse(header + result.displayText + slackNote + actionReminder);
14299
+ const response = textResponse(header + result.displayText + slackNote + recsCloseOut + actionReminder);
14234
14300
  return {
14235
14301
  ...response,
14236
14302
  ...contextBytes !== void 0 ? { _contextBytes: contextBytes } : {},
@@ -16456,7 +16522,7 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}) {
16456
16522
  }
16457
16523
  const [healthResult, priorCount] = await Promise.all([
16458
16524
  adapter2.getCycleHealth().catch(() => ({ totalCycles: 0 })),
16459
- adapter2.getBuildReportCountForTask(taskId).catch(() => 0)
16525
+ typeof adapter2.getBuildReportCountForTask === "function" ? adapter2.getBuildReportCountForTask(taskId).catch(() => 0) : Promise.resolve(0)
16460
16526
  ]);
16461
16527
  const cycleNumber = healthResult.totalCycles;
16462
16528
  const iterationCount = priorCount + 1;
@@ -17666,6 +17732,7 @@ Re-submit with \`notes: "... Reference: <path>"\` to link one, or ignore if none
17666
17732
  }
17667
17733
 
17668
17734
  // src/tools/bug.ts
17735
+ import os2 from "os";
17669
17736
  init_git();
17670
17737
 
17671
17738
  // src/services/bug.ts
@@ -17714,15 +17781,26 @@ async function captureBug(adapter2, input) {
17714
17781
  }
17715
17782
 
17716
17783
  // src/tools/bug.ts
17784
+ function collectDiagnostics(config2) {
17785
+ return {
17786
+ nodeVersion: process.version,
17787
+ platform: os2.platform(),
17788
+ arch: os2.arch(),
17789
+ adapterType: config2.adapterType,
17790
+ projectRoot: config2.projectRoot,
17791
+ hasEndpoint: !!config2.papiEndpoint,
17792
+ lightMode: config2.lightMode
17793
+ };
17794
+ }
17717
17795
  var bugTool = {
17718
17796
  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.',
17797
+ 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
17798
  inputSchema: {
17721
17799
  type: "object",
17722
17800
  properties: {
17723
17801
  text: {
17724
17802
  type: "string",
17725
- description: "The bug description \u2014 becomes the task title."
17803
+ description: "The bug description \u2014 becomes the task title (default mode) or report description (report mode)."
17726
17804
  },
17727
17805
  severity: {
17728
17806
  type: "string",
@@ -17744,6 +17822,10 @@ var bugTool = {
17744
17822
  phase: {
17745
17823
  type: "string",
17746
17824
  description: "Target phase (default: current active phase)."
17825
+ },
17826
+ report: {
17827
+ type: "boolean",
17828
+ 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
17829
  }
17748
17830
  },
17749
17831
  required: ["text"]
@@ -17754,6 +17836,38 @@ async function handleBug(adapter2, config2, args) {
17754
17836
  if (!text) {
17755
17837
  return errorResponse("text is required \u2014 describe the bug you want to report.");
17756
17838
  }
17839
+ if (args.report === true) {
17840
+ if (!adapter2.submitBugReport) {
17841
+ return errorResponse("Bug reports require a database adapter (pg or proxy). The md adapter does not support bug reports.");
17842
+ }
17843
+ const diagnostics = collectDiagnostics(config2);
17844
+ const notes = args.notes?.trim();
17845
+ if (notes) {
17846
+ diagnostics.userNotes = notes;
17847
+ }
17848
+ let projectId;
17849
+ try {
17850
+ const health = await adapter2.getCycleHealth();
17851
+ projectId = adapter2.projectId ?? "unknown";
17852
+ diagnostics.cycleNumber = health.totalCycles;
17853
+ } catch {
17854
+ projectId = "unknown";
17855
+ }
17856
+ const report = await adapter2.submitBugReport({
17857
+ projectId,
17858
+ description: text,
17859
+ diagnostics,
17860
+ status: "open"
17861
+ });
17862
+ return textResponse(
17863
+ `**Bug report submitted** \u2014 ID: \`${report.id}\`
17864
+
17865
+ Description: ${text}
17866
+ Diagnostics collected: Node ${diagnostics.nodeVersion}, ${diagnostics.platform}/${diagnostics.arch}, adapter=${diagnostics.adapterType}
17867
+
17868
+ This report is visible to PAPI maintainers. No further action needed from you.`
17869
+ );
17870
+ }
17757
17871
  const MAX_NOTES_LENGTH = 2e3;
17758
17872
  let rawNotes = args.notes?.trim();
17759
17873
  let notesTruncated = false;
@@ -17808,10 +17922,14 @@ async function recordAdHoc(adapter2, input) {
17808
17922
  if (!existing) {
17809
17923
  throw new Error(`Task "${input.taskId}" not found on the board. Check the task ID and try again.`);
17810
17924
  }
17811
- await adapter2.updateTask(input.taskId, {
17925
+ const updatePayload = {
17812
17926
  notes: existing.notes ? `${existing.notes}
17813
17927
  [ad-hoc] ${input.notes || "Work recorded via ad_hoc"}` : `[ad-hoc] ${input.notes || "Work recorded via ad_hoc"}`
17814
- });
17928
+ };
17929
+ if (existing.status === "In Progress") {
17930
+ updatePayload.status = "In Review";
17931
+ }
17932
+ await adapter2.updateTask(input.taskId, updatePayload);
17815
17933
  task = await adapter2.getTask(input.taskId);
17816
17934
  } else {
17817
17935
  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-alpha.1",
3
+ "version": "0.7.2",
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",