@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.
@@ -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
- return `feat/cycle-${cycleNumber}-${slug}`;
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
- "getContributorRole",
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 { tier: body.tier ?? "free", monthlyToolCalls: body.monthlyToolCalls ?? 0 };
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 Claude Code
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
  );