@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 +66 -7
- package/dist/prompts.js +8 -1
- package/package.json +2 -2
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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.
|
|
4
|
-
"description": "PAPI MCP server
|
|
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",
|