@papi-ai/server 0.7.35 → 0.7.36

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
@@ -4639,13 +4639,35 @@ import {
4639
4639
 
4640
4640
  // src/config.ts
4641
4641
  import path from "path";
4642
+
4643
+ // src/lib/help-text.ts
4644
+ var PAPI_DISCORD = "https://discord.gg/papi";
4645
+ var PAPI_DOCS = "https://getpapi.ai/docs/install";
4646
+ var HELP_FOOTER = `
4647
+ Need a hand?
4648
+ \u2022 Make sure you're in your project's root folder (where your code lives), then say "run setup".
4649
+ \u2022 Diagnose your setup: npx @papi-ai/server doctor
4650
+ \u2022 Hit a PAPI bug or have an idea? Say "report a bug to PAPI" \u2014 it goes straight to the
4651
+ PAPI team (not your project backlog), with diagnostics attached.
4652
+ \u2022 Get help: ${PAPI_DISCORD} \u2022 Docs: ${PAPI_DOCS}
4653
+ `;
4654
+ var HELP_FOOTER_MD = `
4655
+
4656
+ ## Need a hand?
4657
+ - Make sure you're in your project's **root folder** (where your code lives), then say "run setup".
4658
+ - Diagnose your setup: \`npx @papi-ai/server doctor\`
4659
+ - Hit a PAPI bug or have an idea? Say **"report a bug to PAPI"** \u2014 it goes straight to the PAPI team (not your project backlog), with diagnostics attached.
4660
+ - Get help: [Discord](${PAPI_DISCORD}) \xB7 [Docs](${PAPI_DOCS})
4661
+ `;
4662
+
4663
+ // src/config.ts
4642
4664
  function loadConfig() {
4643
4665
  const projectArgIdx = process.argv.indexOf("--project");
4644
4666
  const configuredRoot = projectArgIdx !== -1 ? process.argv[projectArgIdx + 1] : process.env.PAPI_PROJECT_DIR;
4645
4667
  const projectRoot = configuredRoot ?? process.cwd();
4646
4668
  if (!configuredRoot) {
4647
4669
  process.stderr.write(
4648
- '\nPAPI is running but no project is configured.\nSay "run setup" to get started.\n\n'
4670
+ "\nPAPI is running, but no project is configured here.\n" + HELP_FOOTER + "\n"
4649
4671
  );
4650
4672
  }
4651
4673
  const anthropicApiKey = process.env.PAPI_API_KEY ?? "";
@@ -4693,7 +4715,7 @@ If you intentionally self-host, set PAPI_SELF_HOST=1 in your environment to bypa
4693
4715
  }
4694
4716
  if (databaseUrl && !papiEndpoint && !explicitAdapter && !selfHostOptIn && !dataApiKey) {
4695
4717
  throw new Error(
4696
- 'DATABASE_URL is set but PAPI_ADAPTER is not specified.\n\nIf you want to self-host PAPI against your own database, add to your .mcp.json env:\n "PAPI_ADAPTER": "pg"\n\nIf you are an external user with a PAPI account, remove DATABASE_URL from your config.\nExternal users authenticate via PAPI_DATA_API_KEY \u2014 no DATABASE_URL needed.\n\nInstall guide: https://getpapi.ai/docs/install'
4718
+ 'DATABASE_URL is set but PAPI_ADAPTER is not specified.\n\nIf you want to self-host PAPI against your own database, add to your .mcp.json env:\n "PAPI_ADAPTER": "pg"\n\nIf you are an external user with a PAPI account, remove DATABASE_URL from your config.\nExternal users authenticate via PAPI_DATA_API_KEY \u2014 no DATABASE_URL needed.\n' + HELP_FOOTER
4697
4719
  );
4698
4720
  }
4699
4721
  let adapterType = papiEndpoint ? "pg" : databaseUrl && (explicitAdapter === "pg" || selfHostOptIn) ? "pg" : dataEndpoint ? "proxy" : explicitAdapter ? explicitAdapter : "proxy";
@@ -4712,7 +4734,8 @@ and unlocks dashboard features when you're ready.
4712
4734
  After signing up, add this to your .mcp.json env config:
4713
4735
  "PAPI_USER_ID": "your-email@example.com"
4714
4736
 
4715
- Already have an account? Make sure PAPI_USER_ID is set in your .mcp.json env config.`
4737
+ Already have an account? Make sure PAPI_USER_ID is set in your .mcp.json env config.
4738
+ ` + HELP_FOOTER
4716
4739
  );
4717
4740
  }
4718
4741
  return {
@@ -8484,7 +8507,8 @@ Standard planning cycle with full board review.
8484
8507
  - **P1 High** \u2014 Strategically aligned: directly advances the current horizon, phase, or Active Decision goals.
8485
8508
  - **P2 Medium** \u2014 Valuable but not strategically urgent: quality improvements, efficiency, polish, infra.
8486
8509
  - **P3 Low** \u2014 Nice-to-have, speculative, or future-horizon work.
8487
- Within the same priority level, prefer tasks with the highest **impact-to-effort ratio**. Impact is measured by: (a) strategic alignment \u2014 does it advance the current horizon/phase? (b) unlocks other work \u2014 are tasks blocked by this? (c) user-facing \u2014 does it change what users see? (d) compounds over time \u2014 does it make future cycles faster? A high-impact Medium task beats a low-impact Small task at the same priority level. Justify in 2-3 sentences.
8510
+ Within the same priority level, prefer tasks with the highest **impact-to-effort ratio**. Impact is measured by: (a) strategic alignment \u2014 does it advance the current horizon/phase? (b) unlocks other work \u2014 are tasks blocked by this? (c) user-facing \u2014 does it change what users see? (d) compounds over time \u2014 does it make future cycles faster? A high-impact Medium task beats a low-impact Small task at the same priority level. **Capacity-fit (smaller effort) breaks ties between comparable-impact tasks \u2014 it does NOT outrank impact.** Do NOT let a task's smaller size be the deciding factor that repeatedly defers a higher-impact larger task; that is the exact failure mode this step guards against. Justify in 2-3 sentences.
8511
+ **\u26A0\uFE0F Large-task inclusion guarantee (task-2227, structural rule \u2014 not advisory):** Impact weighting alone has historically lost to capacity-fit \u2014 the planner repeatedly filled cycles with XS/S work and deferred high-impact L/XL P1s (9 P1s stalled 6-21 cycles as of C299; C297 named this the "sequencing inversion"). Enforce a HARD rule: **every cycle MUST contain at least one Large or XL strategically-aligned P1 task, UNLESS no unblocked L/XL P1 exists on the board.** Before finalising the selection, scan the candidate pool: if the selected cycle contains zero L/XL tasks AND \u22651 unblocked L/XL P1 exists in the backlog, you MUST force-include the highest-impact such task \u2014 displacing the lowest-impact small task if the budget requires it (respect the single-theme vs mixed-theme budget from the Cycle-sizing rule below rather than always adding on top of an already-full cycle). Record the outcome in a "Large-task guarantee:" line in \`cycleLogNotes\`: name the task the guarantee pulled in, OR "satisfied \u2014 cycle already contains an L/XL task", OR "no unblocked L/XL P1 on the board". Do NOT skip this because the small tasks look individually attractive \u2014 that attractiveness IS the bias.
8488
8512
  **Blocked tasks:** Tasks with status "Blocked" MUST be skipped during task selection \u2014 they are waiting on external dependencies or gates and cannot be built. Do NOT generate BUILD HANDOFFs for blocked tasks. Do NOT recommend blocked tasks. If a blocked task's gate has been resolved (check the notes and recent build reports), emit a \`boardCorrections\` entry to move it back to Backlog. Report blocked task count in the cycle log.
8489
8513
  **Cycle sizing:** Size the cycle based on what the selected tasks actually require \u2014 not a fixed budget. Select the highest-priority unblocked tasks, estimate each one's effort from its scope, and let the total emerge. The historical average effort from Methodology Trends is a reference point for calibration, not a target or floor. A healthy cycle has 6-10 tasks. Cycles with fewer than 5 tasks require explicit justification in the cycle log \u2014 explain why more tasks could not be included. When the backlog has 10+ tasks, the cycle SHOULD have 6+ tasks \u2014 undersized cycles waste planning overhead relative to the available work. If fewer than 5 tasks qualify after filtering (blocked, deferred, raw), check Deferred tasks \u2014 some may be ready to un-defer via a \`boardCorrections\` entry. A 1-task cycle is almost never correct. Prefer grouping tasks by module or similarity \u2014 reduces context switching and enables shared branches during the build phase.
8490
8514
  **Theme-driven sizing:** Single-theme cycles (all tasks in the same module or epic) can absorb 25-30 effort points because builders maintain context across tasks. Mixed-theme cycles should stay at 15-20 effort points to limit context switching. Use the theme to determine the budget, not a fixed number.
@@ -12016,6 +12040,7 @@ function defaultHint(tool) {
12016
12040
  "plan",
12017
12041
  "strategy_review",
12018
12042
  "orient",
12043
+ "papi",
12019
12044
  "build_execute",
12020
12045
  "review_list",
12021
12046
  "review_submit",
@@ -20239,6 +20264,20 @@ async function captureBug(adapter2, input) {
20239
20264
  }
20240
20265
 
20241
20266
  // src/tools/bug.ts
20267
+ var PAPI_SIGNAL_PATTERNS = [
20268
+ /\bpapi\b/i,
20269
+ /\bmcp\b/i,
20270
+ // Distinctive PAPI MCP tool names (whole-word) — these don't appear in an
20271
+ // everyday project-domain bug report.
20272
+ /\b(orient|build_execute|build_list|build_describe|build_cancel|review_submit|review_list|strategy_review|strategy_change|board_view|board_edit|board_deprioritise|board_reconcile|handoff_generate|doc_register|doc_search|ad_hoc|inventory_sync|scope_brief)\b/i,
20273
+ /\bbuild handoff\b/i,
20274
+ /\bremote connector\b/i
20275
+ ];
20276
+ function looksLikePapiBug(text, notes) {
20277
+ const haystack = `${text}
20278
+ ${notes ?? ""}`;
20279
+ return PAPI_SIGNAL_PATTERNS.some((re) => re.test(haystack));
20280
+ }
20242
20281
  function collectDiagnostics(config2) {
20243
20282
  return {
20244
20283
  nodeVersion: process.version,
@@ -20252,7 +20291,7 @@ function collectDiagnostics(config2) {
20252
20291
  }
20253
20292
  var bugTool = {
20254
20293
  name: "bug",
20255
- description: "Report a bug OR submit an idea to PAPI. Two modes: (1) Default \u2014 creates a Backlog task with severity-based priority for the project board. (2) With report=true \u2014 submits a bug or idea (set `type`) upstream to PAPI maintainers with diagnostics, optionally recording notify-when-fixed and contact-ok consent. Use report=true at a friction moment \u2014 e.g. when a PAPI tool returns a workflow-blocking error. Does not call the Anthropic API.",
20294
+ description: 'Report a bug OR submit an idea. Routing: a bug about PAPI itself (a PAPI tool/MCP error, the connector, a handoff/cycle problem) auto-submits UPSTREAM to PAPI maintainers with diagnostics \u2014 you do NOT need to set report=true for these. A bug in the user\'s OWN project auto-files as a Backlog task on their board. Override the routing explicitly: report=true forces upstream, report=false forces the user\'s own board. Set `type` ("bug"/"idea") and optional notify-when-fixed / contact-ok consent for upstream submissions. Does not call the Anthropic API.',
20256
20295
  annotations: { readOnlyHint: false, destructiveHint: false },
20257
20296
  inputSchema: {
20258
20297
  type: "object",
@@ -20284,7 +20323,7 @@ var bugTool = {
20284
20323
  },
20285
20324
  report: {
20286
20325
  type: "boolean",
20287
- 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."
20326
+ description: "Routing override. Leave UNSET to auto-route: PAPI-product bugs go upstream to maintainers, project-domain bugs go to the user's board. Set true to force an upstream diagnostic submission; set false to force a task on the user's own board (use false when an auto-detected PAPI bug is actually about the user's project)."
20288
20327
  },
20289
20328
  type: {
20290
20329
  type: "string",
@@ -20312,8 +20351,18 @@ async function handleBug(adapter2, config2, args) {
20312
20351
  if (!text) {
20313
20352
  return errorResponse("text is required \u2014 describe the bug you want to report.");
20314
20353
  }
20315
- if (args.report === true) {
20354
+ const notesArg = args.notes;
20355
+ const explicitReport = typeof args.report === "boolean" ? args.report : void 0;
20356
+ const papiBug = looksLikePapiBug(text, notesArg);
20357
+ const autoRoutedUpstream = explicitReport === void 0 && papiBug;
20358
+ const shouldReportUpstream = explicitReport === true || autoRoutedUpstream;
20359
+ if (shouldReportUpstream) {
20316
20360
  if (!adapter2.submitBugReport) {
20361
+ if (autoRoutedUpstream) {
20362
+ return errorResponse(
20363
+ `This looks like a PAPI bug, which belongs upstream with the PAPI maintainers \u2014 but upstream reporting needs a database adapter (pg or proxy) and this session uses the md adapter, which can't submit it. Two options: (1) switch to the hosted/pg setup to report it upstream, or (2) re-run with report=false to file it on your own project board instead.`
20364
+ );
20365
+ }
20317
20366
  return errorResponse("Bug reports require a database adapter (pg or proxy). The md adapter does not support bug reports.");
20318
20367
  }
20319
20368
  const type = args.type === "idea" ? "idea" : "bug";
@@ -20348,13 +20397,16 @@ async function handleBug(adapter2, config2, args) {
20348
20397
  const followUpLine = followUps.length ? `
20349
20398
 
20350
20399
  Noted: ${followUps.join("; ")}.` : "";
20400
+ const autoRouteLine = autoRoutedUpstream ? `
20401
+
20402
+ _Detected this as a PAPI bug and sent it upstream rather than adding it to your own board. If it's actually about your project, re-run with \`report=false\` to file it on your board instead._` : "";
20351
20403
  return textResponse(
20352
20404
  `**${kindLabel} submitted** \u2014 ID: \`${report.id}\`
20353
20405
 
20354
20406
  ${type === "idea" ? "Idea" : "Description"}: ${text}
20355
20407
  Diagnostics collected: Node ${diagnostics.nodeVersion}, ${diagnostics.platform}/${diagnostics.arch}, adapter=${diagnostics.adapterType}
20356
20408
 
20357
- This ${type} is visible to PAPI maintainers.${followUpLine}`
20409
+ This ${type} is visible to PAPI maintainers.${followUpLine}${autoRouteLine}`
20358
20410
  );
20359
20411
  }
20360
20412
  let target = adapter2;
@@ -22745,7 +22797,7 @@ ${lines.join("\n")}`;
22745
22797
 
22746
22798
  // src/lib/version-handshake.ts
22747
22799
  var MIN_PROXY_VERSION = "1.0.0";
22748
- var SERVER_VERSION = "0.7.35";
22800
+ var SERVER_VERSION = "0.7.36";
22749
22801
  function meetsMinVersion(actual, required) {
22750
22802
  const actualParts = actual.split(".").map((n) => parseInt(n, 10));
22751
22803
  const requiredParts = required.split(".").map((n) => parseInt(n, 10));
@@ -23564,6 +23616,11 @@ var orientTool = {
23564
23616
  required: []
23565
23617
  }
23566
23618
  };
23619
+ var papiTool = {
23620
+ ...orientTool,
23621
+ name: "papi",
23622
+ description: 'Say "papi" to check in with Papi \u2014 an alias for `orient`. Run this FIRST at session start: it returns your cycle number, task counts, in-progress/in-review work, strategy-review cadence, a velocity snapshot, and the recommended next action. Identical to `orient` (same inputs, same output); use whichever name you prefer. Read-only.'
23623
+ };
23567
23624
  function countStalledP1(warnings) {
23568
23625
  const stallLine = warnings.find((w) => w.includes("P1 tasks stalled"));
23569
23626
  if (!stallLine) return 0;
@@ -26093,6 +26150,8 @@ var ABUSE_CEILINGS = {
26093
26150
  var DEFAULT_CEILING = ABUSE_CEILINGS.free;
26094
26151
  var ALWAYS_FREE_TOOLS = /* @__PURE__ */ new Set([
26095
26152
  "orient",
26153
+ "papi",
26154
+ // alias for orient (task-2223) — must share its free treatment
26096
26155
  "board_view",
26097
26156
  "build_list",
26098
26157
  "build_describe",
@@ -26242,6 +26301,7 @@ var TOOLS_REQUIRING_PAPI = /* @__PURE__ */ new Set([
26242
26301
  "review_list",
26243
26302
  "review_submit",
26244
26303
  "orient",
26304
+ "papi",
26245
26305
  "hierarchy_update",
26246
26306
  "zoom_out",
26247
26307
  "handoff_generate",
@@ -26273,6 +26333,7 @@ var PAPI_TOOLS = [
26273
26333
  reviewClaimTool,
26274
26334
  initTool,
26275
26335
  orientTool,
26336
+ papiTool,
26276
26337
  hierarchyUpdateTool,
26277
26338
  zoomOutTool,
26278
26339
  docRegisterTool,
@@ -26447,6 +26508,9 @@ function createServer(adapter2, config2) {
26447
26508
  return handleInit(config2, safeArgs);
26448
26509
  case "orient":
26449
26510
  return handleOrient(adapter2, config2, safeArgs);
26511
+ case "papi":
26512
+ return handleOrient(adapter2, config2, safeArgs);
26513
+ // alias (task-2223)
26450
26514
  case "hierarchy_update":
26451
26515
  return handleHierarchyUpdate(adapter2, safeArgs);
26452
26516
  case "zoom_out":
@@ -26518,7 +26582,7 @@ function createServer(adapter2, config2) {
26518
26582
  }
26519
26583
  throw err;
26520
26584
  }
26521
- if (name === "orient" && adapter2.getMeteredUsage && !result.content.some((c) => c.text.startsWith("Error:"))) {
26585
+ if ((name === "orient" || name === "papi") && adapter2.getMeteredUsage && !result.content.some((c) => c.text.startsWith("Error:"))) {
26522
26586
  try {
26523
26587
  const decision = await checkMeter(adapter2, "plan", config2.projectId ?? "session");
26524
26588
  if (decision.usage) {
@@ -27108,7 +27172,7 @@ If you haven't set up PAPI yet:
27108
27172
  2. Complete the onboarding wizard \u2014 it generates your config
27109
27173
  3. Copy the config to your project and restart your AI tool
27110
27174
 
27111
- If you already have an account, check that both **PAPI_PROJECT_ID** and **PAPI_DATA_API_KEY** are set in your .mcp.json env config.`
27175
+ If you already have an account, check that both **PAPI_PROJECT_ID** and **PAPI_DATA_API_KEY** are set in your .mcp.json env config.` + HELP_FOOTER_MD
27112
27176
  }]
27113
27177
  }));
27114
27178
  }
package/dist/prompts.js CHANGED
@@ -343,7 +343,8 @@ Standard planning cycle with full board review.
343
343
  - **P1 High** \u2014 Strategically aligned: directly advances the current horizon, phase, or Active Decision goals.
344
344
  - **P2 Medium** \u2014 Valuable but not strategically urgent: quality improvements, efficiency, polish, infra.
345
345
  - **P3 Low** \u2014 Nice-to-have, speculative, or future-horizon work.
346
- Within the same priority level, prefer tasks with the highest **impact-to-effort ratio**. Impact is measured by: (a) strategic alignment \u2014 does it advance the current horizon/phase? (b) unlocks other work \u2014 are tasks blocked by this? (c) user-facing \u2014 does it change what users see? (d) compounds over time \u2014 does it make future cycles faster? A high-impact Medium task beats a low-impact Small task at the same priority level. Justify in 2-3 sentences.
346
+ Within the same priority level, prefer tasks with the highest **impact-to-effort ratio**. Impact is measured by: (a) strategic alignment \u2014 does it advance the current horizon/phase? (b) unlocks other work \u2014 are tasks blocked by this? (c) user-facing \u2014 does it change what users see? (d) compounds over time \u2014 does it make future cycles faster? A high-impact Medium task beats a low-impact Small task at the same priority level. **Capacity-fit (smaller effort) breaks ties between comparable-impact tasks \u2014 it does NOT outrank impact.** Do NOT let a task's smaller size be the deciding factor that repeatedly defers a higher-impact larger task; that is the exact failure mode this step guards against. Justify in 2-3 sentences.
347
+ **\u26A0\uFE0F Large-task inclusion guarantee (task-2227, structural rule \u2014 not advisory):** Impact weighting alone has historically lost to capacity-fit \u2014 the planner repeatedly filled cycles with XS/S work and deferred high-impact L/XL P1s (9 P1s stalled 6-21 cycles as of C299; C297 named this the "sequencing inversion"). Enforce a HARD rule: **every cycle MUST contain at least one Large or XL strategically-aligned P1 task, UNLESS no unblocked L/XL P1 exists on the board.** Before finalising the selection, scan the candidate pool: if the selected cycle contains zero L/XL tasks AND \u22651 unblocked L/XL P1 exists in the backlog, you MUST force-include the highest-impact such task \u2014 displacing the lowest-impact small task if the budget requires it (respect the single-theme vs mixed-theme budget from the Cycle-sizing rule below rather than always adding on top of an already-full cycle). Record the outcome in a "Large-task guarantee:" line in \`cycleLogNotes\`: name the task the guarantee pulled in, OR "satisfied \u2014 cycle already contains an L/XL task", OR "no unblocked L/XL P1 on the board". Do NOT skip this because the small tasks look individually attractive \u2014 that attractiveness IS the bias.
347
348
  **Blocked tasks:** Tasks with status "Blocked" MUST be skipped during task selection \u2014 they are waiting on external dependencies or gates and cannot be built. Do NOT generate BUILD HANDOFFs for blocked tasks. Do NOT recommend blocked tasks. If a blocked task's gate has been resolved (check the notes and recent build reports), emit a \`boardCorrections\` entry to move it back to Backlog. Report blocked task count in the cycle log.
348
349
  **Cycle sizing:** Size the cycle based on what the selected tasks actually require \u2014 not a fixed budget. Select the highest-priority unblocked tasks, estimate each one's effort from its scope, and let the total emerge. The historical average effort from Methodology Trends is a reference point for calibration, not a target or floor. A healthy cycle has 6-10 tasks. Cycles with fewer than 5 tasks require explicit justification in the cycle log \u2014 explain why more tasks could not be included. When the backlog has 10+ tasks, the cycle SHOULD have 6+ tasks \u2014 undersized cycles waste planning overhead relative to the available work. If fewer than 5 tasks qualify after filtering (blocked, deferred, raw), check Deferred tasks \u2014 some may be ready to un-defer via a \`boardCorrections\` entry. A 1-task cycle is almost never correct. Prefer grouping tasks by module or similarity \u2014 reduces context switching and enables shared branches during the build phase.
349
350
  **Theme-driven sizing:** Single-theme cycles (all tasks in the same module or epic) can absorb 25-30 effort points because builders maintain context across tasks. Mixed-theme cycles should stay at 15-20 effort points to limit context switching. Use the theme to determine the budget, not a fixed number.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papi-ai/server",
3
- "version": "0.7.35",
3
+ "version": "0.7.36",
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
  "mcpName": "io.github.cathalos92/papi",