@papi-ai/server 0.7.83 → 0.7.85
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/backfill-cycle-metrics.js +98 -15
- package/dist/index.js +1208 -241
- package/dist/prompts.js +7 -3
- package/package.json +4 -4
|
@@ -135,7 +135,21 @@ var init_dist = __esm({
|
|
|
135
135
|
{ key: "autoBranch", label: "Auto branch", description: "Creates a feature branch per task/cycle at build start.", step: "build" },
|
|
136
136
|
{ key: "autoCommit", label: "Auto commit", description: "Commits your work automatically when a build completes.", step: "build" },
|
|
137
137
|
{ key: "autoPush", label: "Auto push & PR", description: "Pushes the branch and opens a pull request on build complete.", step: "build" },
|
|
138
|
-
{ key: "papiMetaFraming", label: "PAPI self-referential framing", description: "Includes PAPI-meta build-discipline framing in handoff output.", step: "build" }
|
|
138
|
+
{ key: "papiMetaFraming", label: "PAPI self-referential framing", description: "Includes PAPI-meta build-discipline framing in handoff output.", step: "build" },
|
|
139
|
+
// Editor-equal release. Two legitimate team shapes need opposite answers, so
|
|
140
|
+
// this is a PROJECT policy rather than a global rule:
|
|
141
|
+
//
|
|
142
|
+
// OFF (default) — an editor's release opens a CONTRIBUTOR PR and PAPI records
|
|
143
|
+
// the release when GitHub merges it. Right for an open//multi-contributor
|
|
144
|
+
// product where the project owner is the last gate before production.
|
|
145
|
+
// ON — an editor releases on exactly the owner's path. Right for a small team
|
|
146
|
+
// of trusted peers who each run whole cycles end to end, where routing every
|
|
147
|
+
// close back through one person is the bottleneck, not the safeguard.
|
|
148
|
+
//
|
|
149
|
+
// Defaults OFF so existing projects are byte-identical, and because widening who
|
|
150
|
+
// can ship to production must be a deliberate choice, never a silent upgrade.
|
|
151
|
+
// A VIEWER is denied either way — this moves the editor line only.
|
|
152
|
+
{ key: "editorRelease", label: "Editors release like the owner", description: "Lets editors run a full release instead of opening a contributor PR.", step: "release", defaultEnabled: false }
|
|
139
153
|
];
|
|
140
154
|
CAPABILITY_KEYS = CAPABILITY_REGISTRY.map((c) => c.key);
|
|
141
155
|
HOSTED_APP_URL = "https://getpapi.ai";
|
|
@@ -171,6 +185,7 @@ __export(git_exports, {
|
|
|
171
185
|
detectUnrecordedCommits: () => detectUnrecordedCommits,
|
|
172
186
|
ensureLatestDevelop: () => ensureLatestDevelop,
|
|
173
187
|
ensureTagAtHead: () => ensureTagAtHead,
|
|
188
|
+
findContributorReleasePullRequests: () => findContributorReleasePullRequests,
|
|
174
189
|
findTaskCommitsOnBase: () => findTaskCommitsOnBase,
|
|
175
190
|
getBranchDiff: () => getBranchDiff,
|
|
176
191
|
getCommitFiles: () => getCommitFiles,
|
|
@@ -188,6 +203,7 @@ __export(git_exports, {
|
|
|
188
203
|
getOriginRepoSlug: () => getOriginRepoSlug,
|
|
189
204
|
getOriginUrl: () => getOriginUrl,
|
|
190
205
|
getPathsDifferingFrom: () => getPathsDifferingFrom,
|
|
206
|
+
getPullRequestState: () => getPullRequestState,
|
|
191
207
|
getPullRequestUrl: () => getPullRequestUrl,
|
|
192
208
|
getRemoteBranchFiles: () => getRemoteBranchFiles,
|
|
193
209
|
getRootCommitHash: () => getRootCommitHash,
|
|
@@ -214,6 +230,7 @@ __export(git_exports, {
|
|
|
214
230
|
listGroupedCycleBranches: () => listGroupedCycleBranches,
|
|
215
231
|
listOpenPullRequests: () => listOpenPullRequests,
|
|
216
232
|
listOrphanFeatBranches: () => listOrphanFeatBranches,
|
|
233
|
+
memberBranchSlug: () => memberBranchSlug,
|
|
217
234
|
mergePullRequest: () => mergePullRequest,
|
|
218
235
|
normalizeGitUrl: () => normalizeGitUrl,
|
|
219
236
|
pickModuleCycleBranch: () => pickModuleCycleBranch,
|
|
@@ -1106,9 +1123,20 @@ function detectUnrecordedCommits(cwd, baseBranch) {
|
|
|
1106
1123
|
function taskBranchName(taskId) {
|
|
1107
1124
|
return `feat/${taskId}`;
|
|
1108
1125
|
}
|
|
1109
|
-
function cycleBranchName(cycleNumber, module) {
|
|
1126
|
+
function cycleBranchName(cycleNumber, module, memberSlug) {
|
|
1110
1127
|
const slug = module.toLowerCase().replace(/&/g, "and").replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1111
|
-
|
|
1128
|
+
const member = memberSlug ? `-${memberSlug}` : "";
|
|
1129
|
+
return `feat/cycle-${cycleNumber}-${slug}${member}`;
|
|
1130
|
+
}
|
|
1131
|
+
function memberBranchSlug(member) {
|
|
1132
|
+
const sanitise = (raw) => raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 20).replace(/-+$/g, "");
|
|
1133
|
+
const fromName = member.displayName ? sanitise(member.displayName) : "";
|
|
1134
|
+
if (fromName) return fromName;
|
|
1135
|
+
const localPart = member.email ? member.email.split("@")[0] : "";
|
|
1136
|
+
const fromEmail = localPart ? sanitise(localPart) : "";
|
|
1137
|
+
if (fromEmail) return fromEmail;
|
|
1138
|
+
const fromId = member.userId ? sanitise(member.userId).slice(0, 8) : "";
|
|
1139
|
+
return fromId || void 0;
|
|
1112
1140
|
}
|
|
1113
1141
|
function getHeadCommitSha(cwd) {
|
|
1114
1142
|
try {
|
|
@@ -1139,6 +1167,56 @@ function getPullRequestUrl(cwd, branch) {
|
|
|
1139
1167
|
return null;
|
|
1140
1168
|
}
|
|
1141
1169
|
}
|
|
1170
|
+
function getPullRequestState(cwd, prUrl) {
|
|
1171
|
+
try {
|
|
1172
|
+
const output = execFileSync(
|
|
1173
|
+
"gh",
|
|
1174
|
+
["pr", "view", prUrl, "--json", "url,state,mergedAt"],
|
|
1175
|
+
{ cwd, encoding: "utf-8" }
|
|
1176
|
+
).trim();
|
|
1177
|
+
if (!output) return null;
|
|
1178
|
+
const parsed = JSON.parse(output);
|
|
1179
|
+
if (!parsed.url || !["OPEN", "CLOSED", "MERGED"].includes(parsed.state ?? "")) return null;
|
|
1180
|
+
return {
|
|
1181
|
+
url: parsed.url,
|
|
1182
|
+
state: parsed.state,
|
|
1183
|
+
mergedAt: parsed.mergedAt ?? null
|
|
1184
|
+
};
|
|
1185
|
+
} catch {
|
|
1186
|
+
return null;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
function findContributorReleasePullRequests(cwd, cycle) {
|
|
1190
|
+
if (!Number.isInteger(cycle) || cycle <= 0) return [];
|
|
1191
|
+
try {
|
|
1192
|
+
const output = execFileSync(
|
|
1193
|
+
"gh",
|
|
1194
|
+
[
|
|
1195
|
+
"pr",
|
|
1196
|
+
"list",
|
|
1197
|
+
"--state",
|
|
1198
|
+
"all",
|
|
1199
|
+
"--limit",
|
|
1200
|
+
"20",
|
|
1201
|
+
"--search",
|
|
1202
|
+
`"Contributor release PR for cycle ${cycle}" in:body`,
|
|
1203
|
+
"--json",
|
|
1204
|
+
"url,state,mergedAt,headRefName"
|
|
1205
|
+
],
|
|
1206
|
+
{ cwd, encoding: "utf-8" }
|
|
1207
|
+
).trim();
|
|
1208
|
+
if (!output) return [];
|
|
1209
|
+
const parsed = JSON.parse(output);
|
|
1210
|
+
return parsed.flatMap((pr) => pr.url && pr.headRefName && ["OPEN", "CLOSED", "MERGED"].includes(pr.state ?? "") ? [{
|
|
1211
|
+
url: pr.url,
|
|
1212
|
+
state: pr.state,
|
|
1213
|
+
mergedAt: pr.mergedAt ?? null,
|
|
1214
|
+
branch: pr.headRefName
|
|
1215
|
+
}] : []);
|
|
1216
|
+
} catch {
|
|
1217
|
+
return [];
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1142
1220
|
function squashMergePullRequest(cwd, branch) {
|
|
1143
1221
|
const repo = getOriginRepoSlug(cwd);
|
|
1144
1222
|
const baseArgs = ["pr", "merge", branch, "--squash", "--delete-branch"];
|
|
@@ -1231,9 +1309,9 @@ function isBranchContentAlreadyInBase(cwd, branch, baseBranch) {
|
|
|
1231
1309
|
return false;
|
|
1232
1310
|
}
|
|
1233
1311
|
}
|
|
1234
|
-
function pickModuleCycleBranch(candidates, cycleNumber, module) {
|
|
1312
|
+
function pickModuleCycleBranch(candidates, cycleNumber, module, memberSlug) {
|
|
1235
1313
|
if (candidates.length === 0) return void 0;
|
|
1236
|
-
const expected = cycleBranchName(cycleNumber, module);
|
|
1314
|
+
const expected = cycleBranchName(cycleNumber, module, memberSlug);
|
|
1237
1315
|
return candidates.find((b) => b === expected);
|
|
1238
1316
|
}
|
|
1239
1317
|
function listOrphanFeatBranches(cwd, baseBranch) {
|
|
@@ -1506,6 +1584,10 @@ var init_proxy_adapter = __esm({
|
|
|
1506
1584
|
"dismissRecommendation",
|
|
1507
1585
|
"findPendingDocActionsForTask",
|
|
1508
1586
|
"getActiveDecisions",
|
|
1587
|
+
"getContributorRole",
|
|
1588
|
+
"listContributorReleasePrs",
|
|
1589
|
+
"recordContributorReleasePr",
|
|
1590
|
+
"setContributorReleasePrStatus",
|
|
1509
1591
|
"getActiveStage",
|
|
1510
1592
|
"getBuildReportCountForTask",
|
|
1511
1593
|
"getBuildReportsSince",
|
|
@@ -1645,7 +1727,7 @@ var init_proxy_adapter = __esm({
|
|
|
1645
1727
|
"applyActiveDecisionUpdates",
|
|
1646
1728
|
// getToolCallCount + updatePhaseStatus are ABSENT: they now have edge handlers and
|
|
1647
1729
|
// are served through the forwarder (getToolCallCount = SUP-2026-026 handler #1).
|
|
1648
|
-
|
|
1730
|
+
// getContributorRole is edge-wired and binds identity from the bearer.
|
|
1649
1731
|
// task-3018 (C356): storeDocBody + getDocBodyUsage are now WIRED — edge case
|
|
1650
1732
|
// handlers + ALLOWED_METHODS/WRITE_METHODS entries exist, so they forward and
|
|
1651
1733
|
// hosted callers get body storage. Removed from this list, as task-3017 required.
|
|
@@ -1653,9 +1735,6 @@ var init_proxy_adapter = __esm({
|
|
|
1653
1735
|
// of the owner-action queue. Six readers were wired C329 (task-2412) but the
|
|
1654
1736
|
// producer stayed here, so the hosted Owner Action Queue was structurally empty
|
|
1655
1737
|
// (AD-74 inverted). Now forwards; the edge binds user_id to the bearer ([C]).
|
|
1656
|
-
"recordContributorReleasePr",
|
|
1657
|
-
"setContributorReleasePrStatus",
|
|
1658
|
-
"listContributorReleasePrs",
|
|
1659
1738
|
"claimReview",
|
|
1660
1739
|
"getSiblingAds",
|
|
1661
1740
|
"getSiblingRepoTasks",
|
|
@@ -2371,8 +2450,8 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
2371
2450
|
getCycleLearningPatterns() {
|
|
2372
2451
|
return this.invoke("getCycleLearningPatterns", []);
|
|
2373
2452
|
}
|
|
2374
|
-
updateCycleLearningActionRef(learningId, taskDisplayId) {
|
|
2375
|
-
return this.invoke("updateCycleLearningActionRef", [learningId, taskDisplayId]);
|
|
2453
|
+
updateCycleLearningActionRef(learningId, taskDisplayId, opts) {
|
|
2454
|
+
return this.invoke("updateCycleLearningActionRef", [learningId, taskDisplayId, opts ?? {}]);
|
|
2376
2455
|
}
|
|
2377
2456
|
// --- Strategy Review Drafts ---
|
|
2378
2457
|
savePendingReviewResponse(cycleNumber, rawResponse) {
|
|
@@ -2431,8 +2510,8 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
2431
2510
|
async listContributors() {
|
|
2432
2511
|
return this.invoke("listContributors", []);
|
|
2433
2512
|
}
|
|
2434
|
-
async addContributorByEmail(email) {
|
|
2435
|
-
return this.invoke("addContributorByEmail", [email]);
|
|
2513
|
+
async addContributorByEmail(email, role) {
|
|
2514
|
+
return this.invoke("addContributorByEmail", [email, role]);
|
|
2436
2515
|
}
|
|
2437
2516
|
async removeContributorByEmail(email) {
|
|
2438
2517
|
return this.invoke("removeContributorByEmail", [email]);
|
|
@@ -2478,7 +2557,11 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
2478
2557
|
*/
|
|
2479
2558
|
async getMeteredUsage() {
|
|
2480
2559
|
const body = await this.postRoute("metering", {});
|
|
2481
|
-
return {
|
|
2560
|
+
return {
|
|
2561
|
+
tier: body.tier ?? "free",
|
|
2562
|
+
monthlyToolCalls: body.monthlyToolCalls ?? 0,
|
|
2563
|
+
projectLimitOverride: body.projectLimitOverride ?? null
|
|
2564
|
+
};
|
|
2482
2565
|
}
|
|
2483
2566
|
async listUserProjects() {
|
|
2484
2567
|
const body = await this.postRoute("project-list", {});
|
|
@@ -2859,7 +2942,7 @@ Fix the PAPI_PROJECT_ID in your .mcp.json (or unset it and let PAPI resolve the
|
|
|
2859
2942
|
Get started in 3 steps:
|
|
2860
2943
|
1. Sign up at ${dashboardUrl ? `${dashboardUrl}/login` : "your configured PAPI dashboard"}
|
|
2861
2944
|
2. Complete the onboarding wizard \u2014 it generates your .mcp.json config
|
|
2862
|
-
3. Download the config, place it in your project root, and restart
|
|
2945
|
+
3. Download the config, place it in your project root, and restart your AI client
|
|
2863
2946
|
|
|
2864
2947
|
Already have an account? Make sure PAPI_DATA_API_KEY is set in your .mcp.json env config.`
|
|
2865
2948
|
);
|