@papi-ai/server 0.7.57 → 0.7.59

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
@@ -8081,7 +8081,9 @@ import {
8081
8081
  // src/universal-frame.ts
8082
8082
  var UNIVERSAL_FRAME = `PAPI gives this project a structured plan \u2192 build \u2192 review cycle, persisted across sessions. Follow it:
8083
8083
 
8084
- 1. ORIENT FIRST. At the start of every session, call \`orient\` (or \`papi\`) before anything else \u2014 it returns the current cycle, what's in flight, and the recommended next action. Re-run it after any context compression.
8084
+ 0. NEW HERE? If PAPI has never been set up for this project (no cycles yet, or \`orient\` says the board is empty), call \`setup\` FIRST \u2014 it generates the Product Brief and scaffolds the workflow. Then run \`plan\` to create the first cycle. This is the required first step for a brand-new project; everything below assumes setup has already run.
8085
+
8086
+ 1. ORIENT FIRST (once set up). At the start of every session, call \`orient\` (or \`papi\`) before anything else \u2014 it returns the current cycle, what's in flight, and the recommended next action. Re-run it after any context compression.
8085
8087
 
8086
8088
  2. THE CYCLE, IN ORDER: \`plan\` (once per cycle) \u2192 \`build_list\` (pick a task) \u2192 \`build_execute <task>\` to start \u2192 implement the task from its BUILD HANDOFF \u2192 \`build_execute\` again to complete with a build report \u2192 \`review_submit\` \u2192 \`release\` when every cycle task is done.
8087
8089
 
@@ -9301,7 +9303,14 @@ function buildPlanUserMessage(ctx) {
9301
9303
  parts.push("### Pre-Assigned Tasks", "", ctx.preAssignedTasks, "");
9302
9304
  }
9303
9305
  if (ctx.codebaseScan) {
9304
- parts.push("### Codebase Scan (existing implementations)", "", ctx.codebaseScan, "");
9306
+ parts.push(
9307
+ "### Codebase Scan (existing implementations)",
9308
+ "",
9309
+ "Any task tagged **PREMISE-UNVERIFIED** is a discovery/auto-triaged item whose premise references code that already exists \u2014 before scheduling it, verify the premise still holds against the live code; if it is already resolved, deprioritise or cancel it rather than spending a build slot re-verifying shipped work.",
9310
+ "",
9311
+ ctx.codebaseScan,
9312
+ ""
9313
+ );
9305
9314
  }
9306
9315
  if (ctx.buildPatterns) {
9307
9316
  parts.push("### Build Patterns", "", ctx.buildPatterns, "");
@@ -10714,6 +10723,10 @@ function extractSearchTerms(title, notes) {
10714
10723
  }
10715
10724
  return terms.slice(0, 8);
10716
10725
  }
10726
+ function isDiscoveryTask(task) {
10727
+ if (task.taskType === "discovery") return true;
10728
+ return /^\s*\[auto-triaged\]/i.test(task.title);
10729
+ }
10717
10730
  function grepForTerm(projectRoot, term) {
10718
10731
  try {
10719
10732
  const result = execSync2(
@@ -10747,19 +10760,24 @@ function scanCodebaseForTasks(projectRoot, tasks) {
10747
10760
  }
10748
10761
  }
10749
10762
  if (matches.length > 0) {
10750
- results.push({ taskId: task.id, terms, matches });
10763
+ results.push({ taskId: task.id, terms, matches, premiseUnverified: isDiscoveryTask(task) });
10751
10764
  }
10752
10765
  if (Date.now() - startTime > 5e3) break;
10753
10766
  }
10754
10767
  if (results.length === 0) return "";
10755
10768
  const elapsed = Date.now() - startTime;
10756
10769
  console.error(`[codebase-scan] scanned ${tasks.length} tasks in ${elapsed}ms \u2014 ${results.length} with matches`);
10770
+ const flaggedCount = results.filter((r) => r.premiseUnverified).length;
10771
+ const flagNote = flaggedCount > 0 ? ` \u2014 ${flaggedCount} discovery/auto-triaged task(s) flagged PREMISE-UNVERIFIED (verify before scheduling)` : "";
10757
10772
  const lines = [
10758
- `Codebase scan found existing implementations for ${results.length}/${tasks.length} candidate tasks (${elapsed}ms):`,
10773
+ `Codebase scan found existing implementations for ${results.length}/${tasks.length} candidate tasks (${elapsed}ms)${flagNote}:`,
10759
10774
  ""
10760
10775
  ];
10761
10776
  for (const result of results) {
10762
10777
  lines.push(`**${result.taskId}:**`);
10778
+ if (result.premiseUnverified) {
10779
+ lines.push(` \u26A0 PREMISE-UNVERIFIED: discovery/auto-triaged task whose premise references code already present in the repo \u2014 verify the premise still holds; it may already be resolved.`);
10780
+ }
10763
10781
  for (const match of result.matches.slice(0, 3)) {
10764
10782
  const fileList = match.files.slice(0, 3).join(", ");
10765
10783
  const moreCount = match.files.length > 3 ? ` (+${match.files.length - 3} more)` : "";
@@ -12643,7 +12661,7 @@ async function preparePlan(adapter2, config2, filters, focus, force, handoffsOnl
12643
12661
  t = startTimer();
12644
12662
  try {
12645
12663
  const scanTasks = await adapter2.queryBoard({ status: ["Backlog", "In Cycle", "Ready"], compact: true });
12646
- const candidates = scanTasks.filter((task) => task.priority !== "P3 Low" && task.scopeClass !== "brief").slice(0, 15).map((task) => ({ id: task.id, title: task.title, notes: task.notes }));
12664
+ const candidates = scanTasks.filter((task) => task.priority !== "P3 Low" && task.scopeClass !== "brief").slice(0, 15).map((task) => ({ id: task.id, title: task.title, notes: task.notes, taskType: task.taskType }));
12647
12665
  const scanResult = scanCodebaseForTasks(config2.projectRoot, candidates);
12648
12666
  if (scanResult) context.codebaseScan = scanResult;
12649
12667
  } catch (err) {
@@ -18830,6 +18848,7 @@ init_telemetry();
18830
18848
  import { writeFile as writeFile3, readFile as readFile5 } from "fs/promises";
18831
18849
  import { join as join10 } from "path";
18832
18850
  import { execFileSync as execFileSync4 } from "child_process";
18851
+ import { isSensitiveChangelogLine } from "@papi-ai/shared";
18833
18852
  init_git();
18834
18853
  var INITIAL_RELEASE_NOTES = `# Changelog
18835
18854
 
@@ -18841,7 +18860,9 @@ All notable changes to this project are documented in this file.
18841
18860
  `;
18842
18861
  function generateChangelogSection(version, commits) {
18843
18862
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
18844
- const filtered = commits.filter((c) => !new RegExp(`^[a-f0-9]+ release: ${version}$`).test(c));
18863
+ const filtered = commits.filter(
18864
+ (c) => !new RegExp(`^[a-f0-9]+ release: ${version}$`).test(c) && !isSensitiveChangelogLine(c)
18865
+ );
18845
18866
  const commitList = filtered.map((c) => `- ${c}`).join("\n");
18846
18867
  return `## ${version} \u2014 ${date}
18847
18868
 
@@ -21842,6 +21863,11 @@ var buildExecuteTool = {
21842
21863
  description: 'IDs of cycle_learnings this build directly resolves or acts on. Use when the BUILD HANDOFF references a prior learning entry (e.g. "addresses learning abc-123"). Links the learning to this task as action_taken=task_created.',
21843
21864
  items: { type: "string" }
21844
21865
  },
21866
+ fixed_issues: {
21867
+ type: "array",
21868
+ description: `cycle_learnings UUIDs of discovered issues this build FIXED. Stamps resolved_at (via the existing discovered_issue_resolve path) so the hub's "What PAPI caught" surface counts them as fixed on the caught\u2192fixed ledger \u2014 triage-and-fix at build time, no separate tool call. Distinct from resolves_learnings, which only LINKS a learning to this task without closing it. Best-effort and idempotent.`,
21869
+ items: { type: "string" }
21870
+ },
21845
21871
  production_verification: {
21846
21872
  type: "object",
21847
21873
  description: "Required when the branch diff touches a trigger surface (install snippets, MCP transport, auth/middleware, OAuth well-known, vercel.json, supabase functions, Dockerfile, Procfile, env declarations). Record exactly what you verified against the live deploy. http_status MUST be 2xx \u2014 non-2xx rejects the build. Server inspects the diff and enforces this, so passing it on non-trigger tasks is harmless but unnecessary.",
@@ -22042,6 +22068,10 @@ async function handleBuildExecute(adapter2, config2, args, clientName) {
22042
22068
  }
22043
22069
  const tracker = new ProgressTracker("start_build").bindStream(adapter2, { stage: "build", taskId });
22044
22070
  try {
22071
+ const scopeTask = await adapter2.getTask(taskId).catch(() => null);
22072
+ if (scopeTask) {
22073
+ tracker.setStreamScope({ taskId: scopeTask.displayId ?? scopeTask.id, cycle: scopeTask.cycle ?? null });
22074
+ }
22045
22075
  await tracker.recordStep("started");
22046
22076
  const result = await startBuild(adapter2, config2, taskId, { light }, clientName);
22047
22077
  tracker.setStreamScope({ taskId: result.task.displayId ?? result.task.id, cycle: result.task.cycle ?? null });
@@ -22154,6 +22184,7 @@ async function handleExecuteComplete(adapter2, config2, taskId, args, light = fa
22154
22184
  const deadEnds = args.dead_ends;
22155
22185
  const rawBriefImplications = args.brief_implications;
22156
22186
  const resolvesLearnings = Array.isArray(args.resolves_learnings) ? args.resolves_learnings : void 0;
22187
+ const fixedIssues = Array.isArray(args.fixed_issues) ? args.fixed_issues : void 0;
22157
22188
  const rawPreview = args.preview;
22158
22189
  const preview = rawPreview ? {
22159
22190
  urls: Array.isArray(rawPreview.urls) ? rawPreview.urls.filter((u) => typeof u === "string") : void 0,
@@ -22240,6 +22271,16 @@ async function handleExecuteComplete(adapter2, config2, taskId, args, light = fa
22240
22271
  metadata: { learningsLinkedCount: result.learningsLinkedCount }
22241
22272
  });
22242
22273
  }
22274
+ let fixedResolvedCount = 0;
22275
+ if (fixedIssues && fixedIssues.length > 0 && typeof adapter2.markCycleLearningResolved === "function") {
22276
+ for (const learningId of fixedIssues) {
22277
+ try {
22278
+ await adapter2.markCycleLearningResolved(learningId, clientName);
22279
+ fixedResolvedCount++;
22280
+ } catch {
22281
+ }
22282
+ }
22283
+ }
22243
22284
  await tracker.recordStep("moving-to-review", {
22244
22285
  metadata: { status: result.task.status }
22245
22286
  });
@@ -22265,7 +22306,10 @@ async function handleExecuteComplete(adapter2, config2, taskId, args, light = fa
22265
22306
  batchRollupNote = "";
22266
22307
  }
22267
22308
  }
22268
- return textResponse(formatCompleteResult(result) + docsNote + batchRollupNote);
22309
+ const fixedNote = fixedResolvedCount > 0 ? `
22310
+
22311
+ \u2705 Marked ${fixedResolvedCount} discovered issue(s) FIXED \u2014 resolved_at stamped, now counted as fixed on the hub's caught\u2192fixed ledger.` : "";
22312
+ return textResponse(formatCompleteResult(result) + fixedNote + docsNote + batchRollupNote);
22269
22313
  } catch (err) {
22270
22314
  const message = err instanceof Error ? err.message : String(err);
22271
22315
  if (isBuildPushError(err)) {
@@ -29212,6 +29256,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
29212
29256
  var BEARER_PREFIX = "papi_";
29213
29257
  var BEARER_REGEX = /^(papi_|papi_oauth_)[a-f0-9]{64}$/;
29214
29258
  var RESOURCE_METADATA_URL = process.env["PAPI_RESOURCE_METADATA_URL"] ?? "https://getpapi.ai/.well-known/oauth-protected-resource";
29259
+ var GLAMA_MAINTAINER_EMAIL = process.env["GLAMA_MAINTAINER_EMAIL"] ?? "cathal@getpapi.ai";
29215
29260
  var DASHBOARD_ORIGIN = process.env["PAPI_DASHBOARD_URL"] ?? "https://getpapi.ai";
29216
29261
  var MCP_RESOURCE_URL = process.env["NEXT_PUBLIC_MCP_URL"] ?? "https://mcp.getpapi.ai";
29217
29262
  var FRIENDLY_GET_HTML = `<!doctype html>
@@ -29357,6 +29402,20 @@ function startHttpTransport(opts) {
29357
29402
  );
29358
29403
  return;
29359
29404
  }
29405
+ if (req.method === "GET" && req.url === "/.well-known/glama.json") {
29406
+ res.writeHead(200, {
29407
+ "Content-Type": "application/json",
29408
+ "Cache-Control": "public, max-age=3600",
29409
+ ...cors
29410
+ });
29411
+ res.end(
29412
+ JSON.stringify({
29413
+ $schema: "https://glama.ai/mcp/schemas/connector.json",
29414
+ maintainers: [{ email: GLAMA_MAINTAINER_EMAIL }]
29415
+ })
29416
+ );
29417
+ return;
29418
+ }
29360
29419
  if (req.method === "GET" && req.url === "/.well-known/oauth-authorization-server") {
29361
29420
  res.writeHead(302, {
29362
29421
  Location: `${DASHBOARD_ORIGIN}/.well-known/oauth-authorization-server`,
package/dist/prompts.js CHANGED
@@ -529,7 +529,14 @@ function buildPlanUserMessage(ctx) {
529
529
  parts.push("### Pre-Assigned Tasks", "", ctx.preAssignedTasks, "");
530
530
  }
531
531
  if (ctx.codebaseScan) {
532
- parts.push("### Codebase Scan (existing implementations)", "", ctx.codebaseScan, "");
532
+ parts.push(
533
+ "### Codebase Scan (existing implementations)",
534
+ "",
535
+ "Any task tagged **PREMISE-UNVERIFIED** is a discovery/auto-triaged item whose premise references code that already exists \u2014 before scheduling it, verify the premise still holds against the live code; if it is already resolved, deprioritise or cancel it rather than spending a build slot re-verifying shipped work.",
536
+ "",
537
+ ctx.codebaseScan,
538
+ ""
539
+ );
533
540
  }
534
541
  if (ctx.buildPatterns) {
535
542
  parts.push("### Build Patterns", "", ctx.buildPatterns, "");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@papi-ai/server",
3
- "version": "0.7.57",
4
- "description": "PAPI MCP server \u2014 AI-powered sprint planning, build execution, and strategy review for software projects",
3
+ "version": "0.7.59",
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",