@papi-ai/server 0.7.65 → 0.7.67
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 +6 -0
- package/dist/index.js +385 -105
- package/package.json +1 -1
|
@@ -1785,6 +1785,12 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1785
1785
|
markFeedbackNotified(ids, userId) {
|
|
1786
1786
|
return this.invoke("markFeedbackNotified", [ids, userId]);
|
|
1787
1787
|
}
|
|
1788
|
+
// task-2438: the caller's own bug/idea reports. The data-proxy ignores the
|
|
1789
|
+
// client-supplied userId and scopes to the bearer-validated caller, so a user
|
|
1790
|
+
// can only ever read their OWN reports (same pattern as getUnnotifiedResolvedFeedback).
|
|
1791
|
+
listMyBugReports(userId) {
|
|
1792
|
+
return this.invoke("listMyBugReports", [userId]);
|
|
1793
|
+
}
|
|
1788
1794
|
// --- Doc Registry ---
|
|
1789
1795
|
registerDoc(entry) {
|
|
1790
1796
|
return this.invoke("registerDoc", [entry]);
|
package/dist/index.js
CHANGED
|
@@ -1902,6 +1902,12 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1902
1902
|
markFeedbackNotified(ids, userId) {
|
|
1903
1903
|
return this.invoke("markFeedbackNotified", [ids, userId]);
|
|
1904
1904
|
}
|
|
1905
|
+
// task-2438: the caller's own bug/idea reports. The data-proxy ignores the
|
|
1906
|
+
// client-supplied userId and scopes to the bearer-validated caller, so a user
|
|
1907
|
+
// can only ever read their OWN reports (same pattern as getUnnotifiedResolvedFeedback).
|
|
1908
|
+
listMyBugReports(userId) {
|
|
1909
|
+
return this.invoke("listMyBugReports", [userId]);
|
|
1910
|
+
}
|
|
1905
1911
|
// --- Doc Registry ---
|
|
1906
1912
|
registerDoc(entry) {
|
|
1907
1913
|
return this.invoke("registerDoc", [entry]);
|
|
@@ -4334,9 +4340,9 @@ __export(doctor_exports, {
|
|
|
4334
4340
|
__testing: () => __testing,
|
|
4335
4341
|
runDoctor: () => runDoctor
|
|
4336
4342
|
});
|
|
4337
|
-
import { existsSync as
|
|
4343
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
|
|
4338
4344
|
import { homedir as homedir4 } from "os";
|
|
4339
|
-
import { join as
|
|
4345
|
+
import { join as join21 } from "path";
|
|
4340
4346
|
function redact(name, value) {
|
|
4341
4347
|
if (!value) return "(empty)";
|
|
4342
4348
|
if (SECRET_VARS.has(name)) {
|
|
@@ -4347,14 +4353,14 @@ function redact(name, value) {
|
|
|
4347
4353
|
}
|
|
4348
4354
|
function findMcpJson() {
|
|
4349
4355
|
const candidates = [
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4356
|
+
join21(process.cwd(), ".mcp.json"),
|
|
4357
|
+
join21(homedir4(), ".claude", ".mcp.json"),
|
|
4358
|
+
join21(homedir4(), ".mcp.json")
|
|
4353
4359
|
];
|
|
4354
4360
|
for (const path7 of candidates) {
|
|
4355
|
-
if (!
|
|
4361
|
+
if (!existsSync12(path7)) continue;
|
|
4356
4362
|
try {
|
|
4357
|
-
const raw =
|
|
4363
|
+
const raw = readFileSync14(path7, "utf-8");
|
|
4358
4364
|
const parsed = JSON.parse(raw);
|
|
4359
4365
|
const papiEntry = parsed.papi ?? parsed.mcpServers?.papi;
|
|
4360
4366
|
if (!papiEntry) continue;
|
|
@@ -4632,17 +4638,17 @@ __export(reset_exports, {
|
|
|
4632
4638
|
removePapiEntry: () => removePapiEntry,
|
|
4633
4639
|
runReset: () => runReset
|
|
4634
4640
|
});
|
|
4635
|
-
import { existsSync as
|
|
4641
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
|
|
4636
4642
|
import { homedir as homedir5 } from "os";
|
|
4637
|
-
import { join as
|
|
4643
|
+
import { join as join22 } from "path";
|
|
4638
4644
|
import { createInterface } from "readline/promises";
|
|
4639
4645
|
function findResetTarget() {
|
|
4640
4646
|
for (const path7 of CANDIDATE_PATHS()) {
|
|
4641
|
-
if (!
|
|
4647
|
+
if (!existsSync13(path7)) continue;
|
|
4642
4648
|
let raw;
|
|
4643
4649
|
let parsed;
|
|
4644
4650
|
try {
|
|
4645
|
-
raw =
|
|
4651
|
+
raw = readFileSync15(path7, "utf-8");
|
|
4646
4652
|
parsed = JSON.parse(raw);
|
|
4647
4653
|
} catch {
|
|
4648
4654
|
continue;
|
|
@@ -4713,7 +4719,7 @@ async function runReset(args = []) {
|
|
|
4713
4719
|
}
|
|
4714
4720
|
}
|
|
4715
4721
|
try {
|
|
4716
|
-
|
|
4722
|
+
writeFileSync7(target.path, removePapiEntry(target), "utf-8");
|
|
4717
4723
|
process.stdout.write(`
|
|
4718
4724
|
\u2713 Removed papi entry from ${target.path}
|
|
4719
4725
|
`);
|
|
@@ -4730,9 +4736,9 @@ var init_reset = __esm({
|
|
|
4730
4736
|
"src/cli/reset.ts"() {
|
|
4731
4737
|
"use strict";
|
|
4732
4738
|
CANDIDATE_PATHS = () => [
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4739
|
+
join22(process.cwd(), ".mcp.json"),
|
|
4740
|
+
join22(homedir5(), ".claude", ".mcp.json"),
|
|
4741
|
+
join22(homedir5(), ".mcp.json")
|
|
4736
4742
|
];
|
|
4737
4743
|
}
|
|
4738
4744
|
});
|
|
@@ -4743,9 +4749,9 @@ __export(audit_exports, {
|
|
|
4743
4749
|
__testing: () => __testing2,
|
|
4744
4750
|
runAudit: () => runAudit
|
|
4745
4751
|
});
|
|
4746
|
-
import { existsSync as
|
|
4752
|
+
import { existsSync as existsSync14, readFileSync as readFileSync16, readdirSync as readdirSync7 } from "fs";
|
|
4747
4753
|
import { homedir as homedir6 } from "os";
|
|
4748
|
-
import { join as
|
|
4754
|
+
import { join as join23 } from "path";
|
|
4749
4755
|
function safeListDirs(dir) {
|
|
4750
4756
|
try {
|
|
4751
4757
|
return readdirSync7(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort((a, b2) => a.localeCompare(b2));
|
|
@@ -4761,10 +4767,10 @@ function safeListFiles(dir, ext) {
|
|
|
4761
4767
|
}
|
|
4762
4768
|
}
|
|
4763
4769
|
function readMcp(projectPath) {
|
|
4764
|
-
const path7 =
|
|
4765
|
-
if (!
|
|
4770
|
+
const path7 = join23(projectPath, ".mcp.json");
|
|
4771
|
+
if (!existsSync14(path7)) return { servers: [] };
|
|
4766
4772
|
try {
|
|
4767
|
-
const parsed = JSON.parse(
|
|
4773
|
+
const parsed = JSON.parse(readFileSync16(path7, "utf-8"));
|
|
4768
4774
|
const mcpServers = parsed.mcpServers ?? {};
|
|
4769
4775
|
const servers = Object.keys(mcpServers);
|
|
4770
4776
|
if (parsed.papi && !servers.includes("papi")) servers.push("papi");
|
|
@@ -4796,18 +4802,18 @@ function auditProjectSync(projectPath, name) {
|
|
|
4796
4802
|
path: projectPath,
|
|
4797
4803
|
papiProjectId,
|
|
4798
4804
|
mcpServers: servers,
|
|
4799
|
-
skills: safeListDirs(
|
|
4800
|
-
agentSkills: safeListDirs(
|
|
4801
|
-
agents: safeListFiles(
|
|
4802
|
-
hooks: safeListFiles(
|
|
4805
|
+
skills: safeListDirs(join23(projectPath, ".claude", "skills")),
|
|
4806
|
+
agentSkills: safeListDirs(join23(projectPath, ".agents", "skills")),
|
|
4807
|
+
agents: safeListFiles(join23(projectPath, ".claude", "agents"), ".md"),
|
|
4808
|
+
hooks: safeListFiles(join23(projectPath, ".claude", "hooks"), ".sh")
|
|
4803
4809
|
};
|
|
4804
4810
|
}
|
|
4805
4811
|
function discoverProjects() {
|
|
4806
4812
|
const out = [];
|
|
4807
4813
|
for (const root of PROJECT_ROOTS) {
|
|
4808
4814
|
for (const name of safeListDirs(root)) {
|
|
4809
|
-
const path7 =
|
|
4810
|
-
if (
|
|
4815
|
+
const path7 = join23(root, name);
|
|
4816
|
+
if (existsSync14(join23(path7, ".mcp.json")) || existsSync14(join23(path7, ".claude"))) {
|
|
4811
4817
|
out.push({ name, path: path7 });
|
|
4812
4818
|
}
|
|
4813
4819
|
}
|
|
@@ -4818,9 +4824,9 @@ function readGlobalSkills() {
|
|
|
4818
4824
|
return safeListDirs(GLOBAL_SKILLS_DIR);
|
|
4819
4825
|
}
|
|
4820
4826
|
function readGlobalMcpServers() {
|
|
4821
|
-
if (!
|
|
4827
|
+
if (!existsSync14(GLOBAL_CLAUDE_JSON)) return [];
|
|
4822
4828
|
try {
|
|
4823
|
-
const parsed = JSON.parse(
|
|
4829
|
+
const parsed = JSON.parse(readFileSync16(GLOBAL_CLAUDE_JSON, "utf-8"));
|
|
4824
4830
|
const servers = parsed.mcpServers ?? {};
|
|
4825
4831
|
return Object.keys(servers).sort((a, b2) => a.localeCompare(b2));
|
|
4826
4832
|
} catch {
|
|
@@ -4972,9 +4978,9 @@ var PROJECT_ROOTS, GLOBAL_SKILLS_DIR, GLOBAL_CLAUDE_JSON, IDLE_WINDOW_DAYS, GLOB
|
|
|
4972
4978
|
var init_audit = __esm({
|
|
4973
4979
|
"src/cli/audit.ts"() {
|
|
4974
4980
|
"use strict";
|
|
4975
|
-
PROJECT_ROOTS = [
|
|
4976
|
-
GLOBAL_SKILLS_DIR =
|
|
4977
|
-
GLOBAL_CLAUDE_JSON =
|
|
4981
|
+
PROJECT_ROOTS = [join23(homedir6(), "Ai-App-Projects"), join23(homedir6(), "android-projects")];
|
|
4982
|
+
GLOBAL_SKILLS_DIR = join23(homedir6(), ".claude", "skills");
|
|
4983
|
+
GLOBAL_CLAUDE_JSON = join23(homedir6(), ".claude.json");
|
|
4978
4984
|
IDLE_WINDOW_DAYS = 30;
|
|
4979
4985
|
GLOBALIZE_THRESHOLD = 3;
|
|
4980
4986
|
__testing2 = { readMcp, computeFlags, formatReport: formatReport2, discoverProjects, auditProjectSync };
|
|
@@ -4986,8 +4992,8 @@ var setup_exports = {};
|
|
|
4986
4992
|
__export(setup_exports, {
|
|
4987
4993
|
runSetup: () => runSetup
|
|
4988
4994
|
});
|
|
4989
|
-
import { existsSync as
|
|
4990
|
-
import { join as
|
|
4995
|
+
import { existsSync as existsSync15, readFileSync as readFileSync17, writeFileSync as writeFileSync8, chmodSync as chmodSync2, statSync as statSync8 } from "fs";
|
|
4996
|
+
import { join as join24 } from "path";
|
|
4991
4997
|
function baseUrl() {
|
|
4992
4998
|
const fromEnv = process.env["PAPI_HOST"] ?? process.env["PAPI_BASE_URL"];
|
|
4993
4999
|
if (fromEnv) return fromEnv.replace(/\/$/, "");
|
|
@@ -5019,11 +5025,11 @@ function sleep(ms) {
|
|
|
5019
5025
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5020
5026
|
}
|
|
5021
5027
|
function writeMcpJson(opts) {
|
|
5022
|
-
const path7 =
|
|
5028
|
+
const path7 = join24(process.cwd(), ".mcp.json");
|
|
5023
5029
|
let parsed = {};
|
|
5024
|
-
if (
|
|
5030
|
+
if (existsSync15(path7)) {
|
|
5025
5031
|
try {
|
|
5026
|
-
parsed = JSON.parse(
|
|
5032
|
+
parsed = JSON.parse(readFileSync17(path7, "utf-8"));
|
|
5027
5033
|
} catch {
|
|
5028
5034
|
throw new Error(`.mcp.json at ${path7} is not valid JSON. Fix it or remove it before re-running setup.`);
|
|
5029
5035
|
}
|
|
@@ -5044,7 +5050,7 @@ function writeMcpJson(opts) {
|
|
|
5044
5050
|
}
|
|
5045
5051
|
mcpServers.papi = papiEntry;
|
|
5046
5052
|
parsed.mcpServers = mcpServers;
|
|
5047
|
-
|
|
5053
|
+
writeFileSync8(path7, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
5048
5054
|
try {
|
|
5049
5055
|
const mode = statSync8(path7).mode & 511;
|
|
5050
5056
|
if (mode !== 384) chmodSync2(path7, 384);
|
|
@@ -5154,8 +5160,8 @@ var init_setup = __esm({
|
|
|
5154
5160
|
});
|
|
5155
5161
|
|
|
5156
5162
|
// src/index.ts
|
|
5157
|
-
import { readFileSync as
|
|
5158
|
-
import { dirname as dirname6, join as
|
|
5163
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
5164
|
+
import { dirname as dirname6, join as join25, basename as basename2 } from "path";
|
|
5159
5165
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
5160
5166
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5161
5167
|
import { Server as Server2 } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -8193,9 +8199,9 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
8193
8199
|
}
|
|
8194
8200
|
|
|
8195
8201
|
// src/server.ts
|
|
8196
|
-
import { readFileSync as
|
|
8202
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
8197
8203
|
import { access as access4, readdir as readdir4, readFile as readFile9 } from "fs/promises";
|
|
8198
|
-
import { join as
|
|
8204
|
+
import { join as join20, dirname as dirname5 } from "path";
|
|
8199
8205
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8200
8206
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8201
8207
|
import {
|
|
@@ -19255,12 +19261,21 @@ ${command}
|
|
|
19255
19261
|
\`\`\`
|
|
19256
19262
|
PAPI never runs this command itself (AD-58) \u2014 you run it in your own environment. Turn the "Post-release deploy" capability off in the dashboard, or unset PAPI_DEPLOY, to stop this reminder.`;
|
|
19257
19263
|
}
|
|
19264
|
+
function buildPapiMetaFramingDirective(caps, inner) {
|
|
19265
|
+
if (!isCapabilityEnabled(caps, "papiMetaFraming")) return null;
|
|
19266
|
+
return inner;
|
|
19267
|
+
}
|
|
19258
19268
|
|
|
19259
19269
|
// src/services/build.ts
|
|
19260
19270
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
19261
19271
|
import { readdirSync as readdirSync5, existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
19262
19272
|
import { join as join11 } from "path";
|
|
19263
19273
|
|
|
19274
|
+
// src/lib/db-only-notices.ts
|
|
19275
|
+
var DB_ONLY_START_NOTICE = "No git repo detected \u2014 running this cycle in your project database only. Your work stays in the working tree; run `git init` (and add a remote) to enable branches, commits and PR review.";
|
|
19276
|
+
var DB_ONLY_COMPLETE_NOTICE = "No git repo \u2014 build recorded in your project database only (no commit or PR). Run `git init` (and add a remote) to enable git-backed commits and PR review.";
|
|
19277
|
+
var DB_ONLY_RELEASE_NOTICE = "No git repo detected \u2014 this release closed the cycle in your project database only. No tag, branch merge, or CHANGELOG was created. Run `git init` (and add a remote) to enable git-backed releases (tags, merges and changelog).";
|
|
19278
|
+
|
|
19264
19279
|
// src/lib/harness-capability.ts
|
|
19265
19280
|
var HARNESS_REGISTRY = {
|
|
19266
19281
|
// Local stdio CLI agents — PAPI runs git on the user's machine. Confirmed in telemetry.
|
|
@@ -20419,6 +20434,38 @@ Run \`project_switch <slug>\` to switch the active PAPI project, or verify your
|
|
|
20419
20434
|
if (gateDecision.action === "block") {
|
|
20420
20435
|
return errorResponse(gateDecision.message);
|
|
20421
20436
|
}
|
|
20437
|
+
if (!isGitAvailable() || !isGitRepo(config2.projectRoot)) {
|
|
20438
|
+
tracker.mark("db-only-close-cycle");
|
|
20439
|
+
let closed;
|
|
20440
|
+
try {
|
|
20441
|
+
closed = await closeCycleState(config2, adapter2, version, void 0, {
|
|
20442
|
+
force: force ?? false,
|
|
20443
|
+
callerUserId: gate.callerUserId
|
|
20444
|
+
});
|
|
20445
|
+
} catch (err) {
|
|
20446
|
+
return errorResponse(err instanceof Error ? err.message : String(err));
|
|
20447
|
+
}
|
|
20448
|
+
await completeRelease(tracker, {
|
|
20449
|
+
cycleClosed: closed.resolvedCycleNum > 0 ? closed.resolvedCycleNum : null,
|
|
20450
|
+
version,
|
|
20451
|
+
caps,
|
|
20452
|
+
branchMerges: [],
|
|
20453
|
+
changelogEmitted: false
|
|
20454
|
+
});
|
|
20455
|
+
const cyclePart = closed.resolvedCycleNum > 0 ? `Cycle ${closed.resolvedCycleNum}` : "The cycle";
|
|
20456
|
+
const warningsBlock = closed.warnings.length > 0 ? `
|
|
20457
|
+
\u26A0\uFE0F Warnings: ${closed.warnings.join("; ")}
|
|
20458
|
+
` : "";
|
|
20459
|
+
return textResponse(
|
|
20460
|
+
`## Release ${version} \u2014 cycle closed (database only)
|
|
20461
|
+
|
|
20462
|
+
${cyclePart} is now marked **complete** in PAPI, so \`orient\` will no longer flag "Release has not been run."
|
|
20463
|
+
` + warningsBlock + `
|
|
20464
|
+
${DB_ONLY_RELEASE_NOTICE}
|
|
20465
|
+
|
|
20466
|
+
Next: cycle closed! Run \`plan\` to start your next cycle.`
|
|
20467
|
+
);
|
|
20468
|
+
}
|
|
20422
20469
|
tracker.mark("create-release");
|
|
20423
20470
|
const result = await createRelease(config2, branch, version, adapter2, void 0, {
|
|
20424
20471
|
force: force ?? false,
|
|
@@ -21100,8 +21147,12 @@ async function startBuild(adapter2, config2, taskId, options = {}, clientName) {
|
|
|
21100
21147
|
throw err;
|
|
21101
21148
|
}
|
|
21102
21149
|
const branchLines = [];
|
|
21150
|
+
const startCaps = adapter2.getProjectInfo ? (await adapter2.getProjectInfo().catch(() => null))?.capabilities ?? {} : {};
|
|
21151
|
+
const autoBranchEnabled = isCapabilityEnabled(startCaps, "autoBranch");
|
|
21103
21152
|
if (options.light) {
|
|
21104
21153
|
branchLines.push("Light mode: skipping branch creation \u2014 working on current branch.");
|
|
21154
|
+
} else if (!autoBranchEnabled) {
|
|
21155
|
+
branchLines.push("Auto branch: skipped (Auto branch capability off) \u2014 working on current branch.");
|
|
21105
21156
|
} else if (!hasLocalWorkspace()) {
|
|
21106
21157
|
const cycleHealth = await adapter2.getCycleHealth().catch(() => null);
|
|
21107
21158
|
const hosted = hostedStartSteps(clientName, taskId, task.module, cycleHealth?.totalCycles ?? 0, config2.baseBranch);
|
|
@@ -21272,6 +21323,12 @@ async function startBuild(adapter2, config2, taskId, options = {}, clientName) {
|
|
|
21272
21323
|
}
|
|
21273
21324
|
}
|
|
21274
21325
|
}
|
|
21326
|
+
} else {
|
|
21327
|
+
if (!isGitAvailable() || !isGitRepo(config2.projectRoot)) {
|
|
21328
|
+
branchLines.push(DB_ONLY_START_NOTICE);
|
|
21329
|
+
} else {
|
|
21330
|
+
branchLines.push("Auto-commit off (PAPI_AUTO_COMMIT=false) \u2014 skipping branch creation; working on the current branch.");
|
|
21331
|
+
}
|
|
21275
21332
|
}
|
|
21276
21333
|
if (task.status !== "In Progress") {
|
|
21277
21334
|
await adapter2.updateTaskStatus(taskId, "In Progress");
|
|
@@ -21408,6 +21465,9 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
21408
21465
|
if (!task) {
|
|
21409
21466
|
throw new Error(`Task "${taskId}" not found on the Cycle Board.`);
|
|
21410
21467
|
}
|
|
21468
|
+
const completeCaps = adapter2.getProjectInfo ? (await adapter2.getProjectInfo().catch(() => null))?.capabilities ?? {} : {};
|
|
21469
|
+
const autoCommitEnabled = isCapabilityEnabled(completeCaps, "autoCommit");
|
|
21470
|
+
const autoPushEnabled = isCapabilityEnabled(completeCaps, "autoPush");
|
|
21411
21471
|
assertDeployVerification(config2, input, { deployingNow: options.light === true });
|
|
21412
21472
|
const [healthResult, priorCount] = await Promise.all([
|
|
21413
21473
|
adapter2.getCycleHealth().catch(() => ({ totalCycles: 0 })),
|
|
@@ -21682,11 +21742,16 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
21682
21742
|
}
|
|
21683
21743
|
}
|
|
21684
21744
|
const statusNote = input.completed === "yes" ? options.light ? `Task "${task.title}" (${taskId}) marked Done (light mode \u2014 no review needed).` : `Task "${task.title}" (${taskId}) marked In Review \u2014 ready for your sign-off via \`review_submit\`.` : `Task "${task.title}" (${taskId}) status unchanged (completed: ${input.completed}).`;
|
|
21745
|
+
const dbOnlyMode = hasLocalWorkspace() && (!isGitAvailable() || !isGitRepo(config2.projectRoot));
|
|
21685
21746
|
let commitLine;
|
|
21686
|
-
if (
|
|
21687
|
-
commitLine =
|
|
21688
|
-
} else {
|
|
21747
|
+
if (!autoCommitEnabled) {
|
|
21748
|
+
commitLine = "Auto-commit: skipped (Auto commit capability off).";
|
|
21749
|
+
} else if (!config2.autoCommit) {
|
|
21689
21750
|
commitLine = "Auto-commit: skipped (PAPI_AUTO_COMMIT=false).";
|
|
21751
|
+
} else if (dbOnlyMode) {
|
|
21752
|
+
commitLine = DB_ONLY_COMPLETE_NOTICE;
|
|
21753
|
+
} else {
|
|
21754
|
+
commitLine = autoCommit(config2, taskId, task.title, task.buildHandoff?.filesLikelyTouched);
|
|
21690
21755
|
}
|
|
21691
21756
|
if (isGitAvailable() && isGitRepo(config2.projectRoot)) {
|
|
21692
21757
|
const sha = getHeadCommitSha(config2.projectRoot);
|
|
@@ -21709,6 +21774,9 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
21709
21774
|
let prLines = [];
|
|
21710
21775
|
if (options.light) {
|
|
21711
21776
|
prLines.push("Light mode: skipping push and PR creation.");
|
|
21777
|
+
} else if (!autoPushEnabled) {
|
|
21778
|
+
prLines.push("Auto push & PR: skipped (Auto push & PR capability off).");
|
|
21779
|
+
} else if (dbOnlyMode) {
|
|
21712
21780
|
} else if (config2.autoCommit && input.completed === "yes") {
|
|
21713
21781
|
prLines = pushAndCreatePR(config2, taskId, task.title, clientName, task.module, cycleNumber);
|
|
21714
21782
|
}
|
|
@@ -21830,8 +21898,10 @@ var OWNER_NAME = process.env["PAPI_OWNER"] ?? "cathalos92";
|
|
|
21830
21898
|
var MODULE_INSTRUCTIONS = {
|
|
21831
21899
|
Dashboard: `**\u26A0\uFE0F MANDATORY \u2014 Dashboard Module Rules (skip = rework)**
|
|
21832
21900
|
|
|
21833
|
-
**STEP 0 \u2014
|
|
21834
|
-
|
|
21901
|
+
**STEP 0 \u2014 If the frontend-design pack is installed, dispatch the design agent for the visual layer (otherwise build it yourself, STEPs 1-4).**
|
|
21902
|
+
This step is conditional \u2014 PAPI does NOT ship the design pack on a hosted/remote connection, so do not assume the agent exists. Check for \`.claude/agents/frontend-design-engineer.md\`:
|
|
21903
|
+
- **Present:** any \`.tsx\` that renders visible UI should be built by the \`frontend-design-engineer\` subagent (dispatch via the Task tool), not inline here. It works in an isolated window loaded with your project's own brand canon (your DESIGN.md / PRODUCT.md), runs design-critique before and after, builds via the \`frontend-design\` / \`impeccable\` skills, self-checks the falsifiable anti-slop blocklist, and verifies in-browser at populated / empty / mobile states. You remain responsible for data, routes, types, and tests \u2014 hand the visual layer to the agent and integrate the report it returns.
|
|
21904
|
+
- **Absent (default on a hosted PAPI harness, and on any project where you haven't opted in):** the pack is opt-in, not required. Install it by running \`setup\` locally on a frontend project (it detects the stack and installs the agent + design-critique skill + guard hook), or copy it from the PAPI server package under \`design-assets/\`. Until then, do NOT dispatch an agent you don't have \u2014 follow STEPs 1-4 below yourself using the generally-available \`frontend-design\` / \`impeccable\` skills.
|
|
21835
21905
|
|
|
21836
21906
|
**STEP 1 \u2014 BEFORE writing any code:**
|
|
21837
21907
|
If you use the \`impeccable\` skill (recommended for dashboard work), it reads two root files for design context: **PRODUCT.md** (strategic \u2014 brand, users, product purpose, design principles) and **DESIGN.md** (visual tokens \u2014 palette, typography, elevation, components). Run \`impeccable init\` to create them if they don't exist yet. Every visual decision must align with these files; if your output contradicts them, it is wrong. (The legacy \`.impeccable.md\` is no longer read by the skill.)
|
|
@@ -21906,6 +21976,7 @@ import { readdirSync as readdirSync6, existsSync as existsSync8, readFileSync as
|
|
|
21906
21976
|
import { join as join12, relative } from "path";
|
|
21907
21977
|
import { homedir as homedir3 } from "os";
|
|
21908
21978
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
21979
|
+
import { docDeletionBlockMessage } from "@papi-ai/shared";
|
|
21909
21980
|
var docRegisterTool = {
|
|
21910
21981
|
name: "doc_register",
|
|
21911
21982
|
description: "Register or update a document in the doc registry. Called after finalising a research/planning doc, or when build_execute detects unregistered docs. Stores metadata and structured summary \u2014 not full content. Re-registering an existing doc updates its summary, tags, actions, type, and status (upsert). Visibility and owner are not changed on re-register.",
|
|
@@ -22300,9 +22371,161 @@ ${userNotes}` : referenceLine;
|
|
|
22300
22371
|
- ${remainingNote}`
|
|
22301
22372
|
);
|
|
22302
22373
|
}
|
|
22374
|
+
var docDeleteTool = {
|
|
22375
|
+
name: "doc_delete",
|
|
22376
|
+
description: "Permanently delete a registered doc. Guarded: ALLOWED only for the doc's creator or the project owner, and only when nothing depends on it \u2014 a delete is BLOCKED if another doc supersedes it, or a task references it (doc_ref) or a linked action. The guard never cascade-deletes dependents; resolve the dependency first. Identify the doc by `doc_path` (preferred) or `doc_id`.",
|
|
22377
|
+
annotations: { title: "Delete Doc", readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
22378
|
+
inputSchema: {
|
|
22379
|
+
type: "object",
|
|
22380
|
+
properties: {
|
|
22381
|
+
doc_path: { type: "string", description: 'Path of the doc to delete (e.g. "docs/research/funding-landscape.md"). Either this or doc_id is required.' },
|
|
22382
|
+
doc_id: { type: "string", description: "UUID of the doc to delete. Either this or doc_path is required." }
|
|
22383
|
+
},
|
|
22384
|
+
required: []
|
|
22385
|
+
}
|
|
22386
|
+
};
|
|
22387
|
+
async function handleDocDelete(adapter2, config2, args) {
|
|
22388
|
+
if (!adapter2.deleteDoc) {
|
|
22389
|
+
return errorResponse("Doc deletion not available \u2014 requires the pg adapter.");
|
|
22390
|
+
}
|
|
22391
|
+
const docPath = args.doc_path?.trim();
|
|
22392
|
+
const docId = args.doc_id?.trim();
|
|
22393
|
+
if (!docPath && !docId) {
|
|
22394
|
+
return errorResponse("Either doc_path or doc_id is required.");
|
|
22395
|
+
}
|
|
22396
|
+
const gate = await resolveOwnerGate(adapter2, config2);
|
|
22397
|
+
const requesterUserId = gate.enforced ? gate.callerUserId : gate.ownerUserId ?? gate.callerUserId;
|
|
22398
|
+
const result = await adapter2.deleteDoc(docId ?? docPath, requesterUserId);
|
|
22399
|
+
if (!result.deleted) {
|
|
22400
|
+
const reason = result.reason ?? "not_authorized";
|
|
22401
|
+
return errorResponse(`Delete blocked \u2014 ${docDeletionBlockMessage(reason)}`);
|
|
22402
|
+
}
|
|
22403
|
+
return textResponse(`**Deleted:** ${docId ?? docPath}`);
|
|
22404
|
+
}
|
|
22405
|
+
var docReorderTool = {
|
|
22406
|
+
name: "doc_reorder",
|
|
22407
|
+
description: "Persist the display order of docs within this project. Pass `doc_ids` as the full desired sequence of doc UUIDs \u2014 each doc's position is saved so the dashboard renders them in that order. Ids not in this project are ignored.",
|
|
22408
|
+
annotations: { title: "Reorder Docs", readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
22409
|
+
inputSchema: {
|
|
22410
|
+
type: "object",
|
|
22411
|
+
properties: {
|
|
22412
|
+
doc_ids: {
|
|
22413
|
+
type: "array",
|
|
22414
|
+
items: { type: "string" },
|
|
22415
|
+
description: "Ordered list of doc UUIDs \u2014 index 0 renders first."
|
|
22416
|
+
}
|
|
22417
|
+
},
|
|
22418
|
+
required: ["doc_ids"]
|
|
22419
|
+
}
|
|
22420
|
+
};
|
|
22421
|
+
async function handleDocReorder(adapter2, args) {
|
|
22422
|
+
if (!adapter2.reorderDocs) {
|
|
22423
|
+
return errorResponse("Doc reorder not available \u2014 requires the pg adapter.");
|
|
22424
|
+
}
|
|
22425
|
+
const docIds = args.doc_ids;
|
|
22426
|
+
if (!Array.isArray(docIds) || docIds.some((id) => typeof id !== "string")) {
|
|
22427
|
+
return errorResponse("doc_ids is required and must be an array of doc UUID strings.");
|
|
22428
|
+
}
|
|
22429
|
+
if (docIds.length === 0) {
|
|
22430
|
+
return errorResponse("doc_ids must contain at least one doc UUID.");
|
|
22431
|
+
}
|
|
22432
|
+
await adapter2.reorderDocs(docIds);
|
|
22433
|
+
return textResponse(`**Reordered ${docIds.length} doc(s).** New order persisted.`);
|
|
22434
|
+
}
|
|
22303
22435
|
|
|
22304
22436
|
// src/tools/build.ts
|
|
22305
22437
|
init_git();
|
|
22438
|
+
|
|
22439
|
+
// src/lib/build-checkpoint.ts
|
|
22440
|
+
import { createHash as createHash4 } from "crypto";
|
|
22441
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
22442
|
+
import { join as join13 } from "path";
|
|
22443
|
+
var BUILD_CHECKPOINT_VERSION = 1;
|
|
22444
|
+
function cwdHash(cwd) {
|
|
22445
|
+
return createHash4("sha256").update(realpathOrSelf(cwd)).digest("hex").slice(0, 12);
|
|
22446
|
+
}
|
|
22447
|
+
function safeTaskId(taskId) {
|
|
22448
|
+
return taskId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
22449
|
+
}
|
|
22450
|
+
function checkpointDir(cwd) {
|
|
22451
|
+
return join13(cwd, ".papi", "state");
|
|
22452
|
+
}
|
|
22453
|
+
function checkpointPath(cwd, taskId) {
|
|
22454
|
+
return join13(checkpointDir(cwd), `build-${safeTaskId(taskId)}.${cwdHash(cwd)}.json`);
|
|
22455
|
+
}
|
|
22456
|
+
function writeBuildCheckpoint(input) {
|
|
22457
|
+
try {
|
|
22458
|
+
const dir = checkpointDir(input.cwd);
|
|
22459
|
+
if (!existsSync9(dir)) {
|
|
22460
|
+
mkdirSync3(dir, { recursive: true });
|
|
22461
|
+
}
|
|
22462
|
+
const checkpoint = {
|
|
22463
|
+
version: BUILD_CHECKPOINT_VERSION,
|
|
22464
|
+
taskId: input.taskId,
|
|
22465
|
+
branch: input.branch,
|
|
22466
|
+
step: input.step,
|
|
22467
|
+
lastCommitSha: input.lastCommitSha,
|
|
22468
|
+
modifiedFiles: input.modifiedFiles,
|
|
22469
|
+
cwd: realpathOrSelf(input.cwd),
|
|
22470
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22471
|
+
};
|
|
22472
|
+
writeFileSync4(checkpointPath(input.cwd, input.taskId), JSON.stringify(checkpoint, null, 2) + "\n", "utf-8");
|
|
22473
|
+
} catch {
|
|
22474
|
+
}
|
|
22475
|
+
}
|
|
22476
|
+
function readBuildCheckpoint(key) {
|
|
22477
|
+
try {
|
|
22478
|
+
const path7 = checkpointPath(key.cwd, key.taskId);
|
|
22479
|
+
if (!existsSync9(path7)) return null;
|
|
22480
|
+
const parsed = JSON.parse(readFileSync8(path7, "utf-8"));
|
|
22481
|
+
if (!parsed || parsed.version !== BUILD_CHECKPOINT_VERSION || parsed.taskId !== key.taskId) {
|
|
22482
|
+
return null;
|
|
22483
|
+
}
|
|
22484
|
+
return {
|
|
22485
|
+
version: BUILD_CHECKPOINT_VERSION,
|
|
22486
|
+
taskId: parsed.taskId,
|
|
22487
|
+
branch: parsed.branch ?? null,
|
|
22488
|
+
step: "branch_ready",
|
|
22489
|
+
lastCommitSha: parsed.lastCommitSha ?? null,
|
|
22490
|
+
modifiedFiles: Array.isArray(parsed.modifiedFiles) ? parsed.modifiedFiles : [],
|
|
22491
|
+
cwd: typeof parsed.cwd === "string" ? parsed.cwd : realpathOrSelf(key.cwd),
|
|
22492
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : ""
|
|
22493
|
+
};
|
|
22494
|
+
} catch {
|
|
22495
|
+
return null;
|
|
22496
|
+
}
|
|
22497
|
+
}
|
|
22498
|
+
function clearBuildCheckpoint(key) {
|
|
22499
|
+
try {
|
|
22500
|
+
const path7 = checkpointPath(key.cwd, key.taskId);
|
|
22501
|
+
if (existsSync9(path7)) {
|
|
22502
|
+
unlinkSync3(path7);
|
|
22503
|
+
}
|
|
22504
|
+
} catch {
|
|
22505
|
+
}
|
|
22506
|
+
}
|
|
22507
|
+
function formatResumeNote(cp) {
|
|
22508
|
+
const files = cp.modifiedFiles.filter((f) => f && f.trim());
|
|
22509
|
+
const fileList = files.length > 0 ? files.slice(0, 12).map((f) => ` - ${f}`).join("\n") + (files.length > 12 ? `
|
|
22510
|
+
- \u2026+${files.length - 12} more` : "") : " _(none recorded)_";
|
|
22511
|
+
const lines = [
|
|
22512
|
+
"> **\u21BB Resuming from checkpoint** \u2014 this task is already In Progress; you are NOT starting clean.",
|
|
22513
|
+
`> - Branch: \`${cp.branch ?? "unknown"}\``,
|
|
22514
|
+
"> - Last step: branch ready",
|
|
22515
|
+
`> - Last commit: ${cp.lastCommitSha ? cp.lastCommitSha.slice(0, 8) : "none"}`,
|
|
22516
|
+
`> - Checkpoint saved: ${cp.updatedAt || "unknown"}`,
|
|
22517
|
+
">",
|
|
22518
|
+
"> Modified files at last checkpoint:",
|
|
22519
|
+
...fileList.split("\n").map((l) => `> ${l}`),
|
|
22520
|
+
">",
|
|
22521
|
+
"> Review the existing branch and changes above before writing new code \u2014 do not re-do work already committed.",
|
|
22522
|
+
"",
|
|
22523
|
+
""
|
|
22524
|
+
];
|
|
22525
|
+
return lines.join("\n");
|
|
22526
|
+
}
|
|
22527
|
+
|
|
22528
|
+
// src/tools/build.ts
|
|
22306
22529
|
var buildListTool = {
|
|
22307
22530
|
name: "build_list",
|
|
22308
22531
|
description: "List cycle tasks that have BUILD HANDOFFs ready for execution. Shows task ID, title, status, priority, and complexity. In Progress tasks appear first, then Backlog. Does not call the Anthropic API.",
|
|
@@ -22640,10 +22863,23 @@ async function handleBuildExecute(adapter2, config2, args, clientName) {
|
|
|
22640
22863
|
if (scopeTask) {
|
|
22641
22864
|
tracker.setStreamScope({ taskId: scopeTask.displayId ?? scopeTask.id, cycle: scopeTask.cycle ?? null });
|
|
22642
22865
|
}
|
|
22866
|
+
let resumeNote = "";
|
|
22867
|
+
if (scopeTask?.status === "In Progress") {
|
|
22868
|
+
const existing = readBuildCheckpoint({ cwd: config2.projectRoot, taskId });
|
|
22869
|
+
if (existing) resumeNote = formatResumeNote(existing);
|
|
22870
|
+
}
|
|
22643
22871
|
await tracker.recordStep("started");
|
|
22644
22872
|
const result = await startBuild(adapter2, config2, taskId, { light }, clientName);
|
|
22645
22873
|
tracker.setStreamScope({ taskId: result.task.displayId ?? result.task.id, cycle: result.task.cycle ?? null });
|
|
22646
22874
|
await tracker.recordStep("branch_ready");
|
|
22875
|
+
writeBuildCheckpoint({
|
|
22876
|
+
cwd: config2.projectRoot,
|
|
22877
|
+
taskId,
|
|
22878
|
+
branch: getCurrentBranch(config2.projectRoot),
|
|
22879
|
+
step: "branch_ready",
|
|
22880
|
+
lastCommitSha: getHeadCommitSha(config2.projectRoot),
|
|
22881
|
+
modifiedFiles: getModifiedFiles(config2.projectRoot)
|
|
22882
|
+
});
|
|
22647
22883
|
tracker.mark("start_decorate_handoff");
|
|
22648
22884
|
const branchInfo = result.branchLines.length > 0 ? result.branchLines.map((l) => `> ${l}`).join("\n") + "\n\n" : "";
|
|
22649
22885
|
const phaseNote = result.phaseChanges.length > 0 ? "\n\n" + result.phaseChanges.map((c) => `Phase auto-updated: ${c.phaseId} ${c.oldStatus} \u2192 ${c.newStatus}`).join("\n") : "";
|
|
@@ -22723,7 +22959,8 @@ If this build fixes any of these, pass their UUIDs in \`fixed_issues\` on comple
|
|
|
22723
22959
|
formatModelRecommendation(result.task.buildHandoff?.effort ?? result.task.complexity)
|
|
22724
22960
|
) ?? "";
|
|
22725
22961
|
const gestaltNote = buildGestaltPreBuildDirective(caps) ?? "";
|
|
22726
|
-
|
|
22962
|
+
const buildDisciplineSection = buildPapiMetaFramingDirective(caps, buildDisciplineNote) ?? "";
|
|
22963
|
+
return textResponse(resumeNote + header + serializeBuildHandoff(result.task.buildHandoff) + modelNote + gestaltNote + adSection + moduleInstructions + moduleContext + dogfoodSection + openIssuesSection + verificationNote + buildDisciplineSection + chainInstruction + phaseNote + filesToWriteSection);
|
|
22727
22964
|
} catch (err) {
|
|
22728
22965
|
if (isNoHandoffError(err)) {
|
|
22729
22966
|
const lines = [
|
|
@@ -22861,6 +23098,7 @@ Your report was NOT discarded and the task is NOT yet Done \u2014 re-send with \
|
|
|
22861
23098
|
preview
|
|
22862
23099
|
}, { light }, clientName);
|
|
22863
23100
|
tracker.mark("complete_format");
|
|
23101
|
+
clearBuildCheckpoint({ cwd: config2.projectRoot, taskId });
|
|
22864
23102
|
tracker.setStreamScope({ taskId: result.task.displayId ?? result.task.id, cycle: result.cycleNumber });
|
|
22865
23103
|
await tracker.recordStep("report_written");
|
|
22866
23104
|
if ((result.autoTriagedCount ?? 0) > 0) {
|
|
@@ -23461,6 +23699,43 @@ This ${type} is visible to PAPI maintainers.${followUpLine}${autoRouteLine}`
|
|
|
23461
23699
|
const truncateWarning = notesTruncated ? ` (notes truncated to ${MAX_NOTES_LENGTH} chars)` : "";
|
|
23462
23700
|
return textResponse(`\u{1F41B} ${task.id}: "${task.title}" \u2014 ${severityLabel} bug added to backlog${overrideNote}${branchNote}. Will be picked up by next plan.${truncateWarning}`);
|
|
23463
23701
|
}
|
|
23702
|
+
var bugListTool = {
|
|
23703
|
+
name: "bug_list",
|
|
23704
|
+
description: "List the upstream bug/idea reports YOU filed to the PAPI maintainers (via the `bug` tool with report=true or an auto-routed PAPI bug), newest first. Read-only. Shows each report's id, kind (bug/idea), triage status, description, and when it was filed. Scoped to your own submissions only \u2014 never another user's. Requires a database adapter (pg or hosted proxy); the local md adapter has no upstream store. Does not call the Anthropic API.",
|
|
23705
|
+
annotations: { title: "List My Bug Reports", readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
23706
|
+
inputSchema: {
|
|
23707
|
+
type: "object",
|
|
23708
|
+
properties: {
|
|
23709
|
+
limit: {
|
|
23710
|
+
type: "integer",
|
|
23711
|
+
description: "Optional maximum number of reports to return. Omit to return all. Reserved for future pagination \u2014 current behaviour returns all your reports newest-first regardless of value."
|
|
23712
|
+
}
|
|
23713
|
+
},
|
|
23714
|
+
required: []
|
|
23715
|
+
}
|
|
23716
|
+
};
|
|
23717
|
+
async function handleBugList(adapter2, config2) {
|
|
23718
|
+
if (!adapter2.listMyBugReports) {
|
|
23719
|
+
return errorResponse(
|
|
23720
|
+
"Listing your bug reports requires a database adapter (pg or hosted proxy). The md adapter has no upstream report store."
|
|
23721
|
+
);
|
|
23722
|
+
}
|
|
23723
|
+
const reports = await adapter2.listMyBugReports(config2.userId);
|
|
23724
|
+
if (reports.length === 0) {
|
|
23725
|
+
return textResponse("You haven't filed any bug or idea reports yet. Use the `bug` tool to report a PAPI bug or suggest an idea.");
|
|
23726
|
+
}
|
|
23727
|
+
const lines = reports.map((r) => {
|
|
23728
|
+
const kind = r.type === "idea" ? "\u{1F4A1} idea" : "\u{1F41B} bug";
|
|
23729
|
+
const when = r.createdAt.slice(0, 10);
|
|
23730
|
+
return `- \`${r.id}\` \xB7 ${kind} \xB7 ${r.status} \xB7 ${when}
|
|
23731
|
+
${r.description}`;
|
|
23732
|
+
});
|
|
23733
|
+
return textResponse(
|
|
23734
|
+
`**Your reports (${reports.length}, newest first)**
|
|
23735
|
+
|
|
23736
|
+
${lines.join("\n")}`
|
|
23737
|
+
);
|
|
23738
|
+
}
|
|
23464
23739
|
|
|
23465
23740
|
// src/tools/ad-hoc.ts
|
|
23466
23741
|
init_git();
|
|
@@ -23707,13 +23982,13 @@ _To correct: board_edit ${result.task.id} with updated fields._`
|
|
|
23707
23982
|
init_git();
|
|
23708
23983
|
|
|
23709
23984
|
// src/services/reconcile.ts
|
|
23710
|
-
import { readFileSync as
|
|
23711
|
-
import { join as
|
|
23985
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
23986
|
+
import { join as join14 } from "path";
|
|
23712
23987
|
function loadDocsIndex(projectRoot) {
|
|
23713
23988
|
if (!hasLocalWorkspace()) return "";
|
|
23714
23989
|
try {
|
|
23715
|
-
const indexPath =
|
|
23716
|
-
const raw =
|
|
23990
|
+
const indexPath = join14(projectRoot, "docs", "INDEX.md");
|
|
23991
|
+
const raw = readFileSync9(indexPath, "utf8");
|
|
23717
23992
|
const rows = raw.split("\n").filter((l) => l.startsWith("| ["));
|
|
23718
23993
|
if (rows.length === 0) return "";
|
|
23719
23994
|
const entries = rows.map((row) => {
|
|
@@ -24288,8 +24563,8 @@ Produce your analysis and structured output above. Present Part 1 to the user an
|
|
|
24288
24563
|
}
|
|
24289
24564
|
|
|
24290
24565
|
// src/tools/review.ts
|
|
24291
|
-
import { existsSync as
|
|
24292
|
-
import { join as
|
|
24566
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
24567
|
+
import { join as join15 } from "path";
|
|
24293
24568
|
init_git();
|
|
24294
24569
|
|
|
24295
24570
|
// src/services/review.ts
|
|
@@ -24518,11 +24793,11 @@ ${task.buildReport}` : "### Build Report\n(none recorded)";
|
|
|
24518
24793
|
${diff}
|
|
24519
24794
|
\`\`\`` : "### Branch diff vs base\n(no diff resolved \u2014 not a git repo, no base ref, or no committed changes)";
|
|
24520
24795
|
let projectContext = "";
|
|
24521
|
-
const ctxPath =
|
|
24522
|
-
if (
|
|
24796
|
+
const ctxPath = join15(config2.projectRoot, ".agents", "papi-context.md");
|
|
24797
|
+
if (existsSync10(ctxPath)) {
|
|
24523
24798
|
try {
|
|
24524
24799
|
projectContext = `### Project context (.agents/papi-context.md)
|
|
24525
|
-
${
|
|
24800
|
+
${readFileSync10(ctxPath, "utf-8")}
|
|
24526
24801
|
|
|
24527
24802
|
`;
|
|
24528
24803
|
} catch {
|
|
@@ -24682,8 +24957,8 @@ function mergeAfterAccept(config2, taskId) {
|
|
|
24682
24957
|
};
|
|
24683
24958
|
}
|
|
24684
24959
|
const details = [];
|
|
24685
|
-
const papiDir =
|
|
24686
|
-
if (
|
|
24960
|
+
const papiDir = join15(config2.projectRoot, ".papi");
|
|
24961
|
+
if (existsSync10(papiDir)) {
|
|
24687
24962
|
try {
|
|
24688
24963
|
const commitResult = stageDirAndCommit(
|
|
24689
24964
|
config2.projectRoot,
|
|
@@ -26265,7 +26540,7 @@ function formatDeferredGateSection(sweep) {
|
|
|
26265
26540
|
|
|
26266
26541
|
// src/tools/agent-list.ts
|
|
26267
26542
|
import { readdir as readdir2, readFile as readFile7 } from "fs/promises";
|
|
26268
|
-
import { join as
|
|
26543
|
+
import { join as join16 } from "path";
|
|
26269
26544
|
var NO_AGENTS_HINT = "No project sub-agents found in `.claude/agents/`. Add a `*.md` file with `name` + `description` frontmatter \u2014 see the 1926-Census marketing sub-agent for a reference implementation.";
|
|
26270
26545
|
function parseAgentFrontmatter(content) {
|
|
26271
26546
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -26277,7 +26552,7 @@ function parseAgentFrontmatter(content) {
|
|
|
26277
26552
|
return { name: nameMatch?.[1].trim(), description };
|
|
26278
26553
|
}
|
|
26279
26554
|
async function listAgents(projectRoot) {
|
|
26280
|
-
const agentsDir =
|
|
26555
|
+
const agentsDir = join16(projectRoot, ".claude", "agents");
|
|
26281
26556
|
let files;
|
|
26282
26557
|
try {
|
|
26283
26558
|
files = await readdir2(agentsDir);
|
|
@@ -26288,7 +26563,7 @@ async function listAgents(projectRoot) {
|
|
|
26288
26563
|
for (const file of files.filter((f) => f.endsWith(".md"))) {
|
|
26289
26564
|
let content;
|
|
26290
26565
|
try {
|
|
26291
|
-
content = await readFile7(
|
|
26566
|
+
content = await readFile7(join16(agentsDir, file), "utf-8");
|
|
26292
26567
|
} catch {
|
|
26293
26568
|
continue;
|
|
26294
26569
|
}
|
|
@@ -26296,7 +26571,7 @@ async function listAgents(projectRoot) {
|
|
|
26296
26571
|
agents.push({
|
|
26297
26572
|
name: meta?.name ?? file.replace(/\.md$/, ""),
|
|
26298
26573
|
description: meta?.description ?? "",
|
|
26299
|
-
path:
|
|
26574
|
+
path: join16(".claude", "agents", file)
|
|
26300
26575
|
});
|
|
26301
26576
|
}
|
|
26302
26577
|
agents.sort((a, b2) => a.name.localeCompare(b2.name));
|
|
@@ -26437,8 +26712,8 @@ async function verifyProject(adapter2) {
|
|
|
26437
26712
|
// src/tools/orient.ts
|
|
26438
26713
|
import { execFile as execFile2 } from "child_process";
|
|
26439
26714
|
import { promisify as promisify2 } from "util";
|
|
26440
|
-
import { readFileSync as
|
|
26441
|
-
import { join as
|
|
26715
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync5, existsSync as existsSync11 } from "fs";
|
|
26716
|
+
import { join as join17 } from "path";
|
|
26442
26717
|
var execFileAsync2 = promisify2(execFile2);
|
|
26443
26718
|
var GIT_DEPENDENT_ENVS = /* @__PURE__ */ new Set(["hosted", "api"]);
|
|
26444
26719
|
var VALID_ENVS = /* @__PURE__ */ new Set(["local-cli", "hosted", "api", "unknown"]);
|
|
@@ -26797,8 +27072,8 @@ async function getLatestGitTag(projectRoot) {
|
|
|
26797
27072
|
}
|
|
26798
27073
|
async function checkNpmVersionDrift() {
|
|
26799
27074
|
try {
|
|
26800
|
-
const pkgPath =
|
|
26801
|
-
const pkg = JSON.parse(
|
|
27075
|
+
const pkgPath = join17(new URL(".", import.meta.url).pathname, "..", "..", "package.json");
|
|
27076
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
26802
27077
|
const localVersion = pkg.version;
|
|
26803
27078
|
const packageName = pkg.name;
|
|
26804
27079
|
const { stdout } = await execFileAsync2("npm", ["view", packageName, "version"], {
|
|
@@ -27472,9 +27747,9 @@ function enrichClaudeMd(projectRoot, cycleNumber, adapterType, collector, client
|
|
|
27472
27747
|
|
|
27473
27748
|
\u{1F4DD} **CLAUDE.md enriched** \u2014 added ${tierNames2.join(" + ")} guidance for cycle ${cycleNumber}+ projects.`;
|
|
27474
27749
|
}
|
|
27475
|
-
const claudeMdPath =
|
|
27476
|
-
if (!
|
|
27477
|
-
const content =
|
|
27750
|
+
const claudeMdPath = join17(projectRoot, "CLAUDE.md");
|
|
27751
|
+
if (!existsSync11(claudeMdPath)) return "";
|
|
27752
|
+
const content = readFileSync11(claudeMdPath, "utf-8");
|
|
27478
27753
|
const additions = [];
|
|
27479
27754
|
if (cycleNumber >= 6 && !content.includes(CLAUDE_MD_ENRICHMENT_SENTINEL_T1)) {
|
|
27480
27755
|
additions.push(dedupeEnrichmentBlob(content, CLAUDE_MD_TIER_1));
|
|
@@ -27483,7 +27758,7 @@ function enrichClaudeMd(projectRoot, cycleNumber, adapterType, collector, client
|
|
|
27483
27758
|
additions.push(dedupeEnrichmentBlob(content, CLAUDE_MD_TIER_2));
|
|
27484
27759
|
}
|
|
27485
27760
|
if (additions.length === 0) return "";
|
|
27486
|
-
|
|
27761
|
+
writeFileSync5(claudeMdPath, content + additions.join(""), "utf-8");
|
|
27487
27762
|
const tierNames = [];
|
|
27488
27763
|
if (additions.some((a) => a.includes(CLAUDE_MD_ENRICHMENT_SENTINEL_T1))) tierNames.push("Established (batch building, strategy reviews, AD lifecycle)");
|
|
27489
27764
|
if (additions.some((a) => a.includes(CLAUDE_MD_ENRICHMENT_SENTINEL_T2))) tierNames.push("Mature (idea pipeline, doc registry, advanced patterns)");
|
|
@@ -28206,11 +28481,11 @@ ${result.userMessage}
|
|
|
28206
28481
|
}
|
|
28207
28482
|
|
|
28208
28483
|
// src/tools/scope-brief.ts
|
|
28209
|
-
import { readFileSync as
|
|
28484
|
+
import { readFileSync as readFileSync12, statSync as statSync7 } from "fs";
|
|
28210
28485
|
|
|
28211
28486
|
// src/services/scope-brief.ts
|
|
28212
|
-
import { writeFileSync as
|
|
28213
|
-
import { join as
|
|
28487
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
28488
|
+
import { join as join18, dirname as dirname4 } from "path";
|
|
28214
28489
|
var SCOPE_BRIEF_SYSTEM = `You are a technical scoping tool. You receive a brief-class task (too large to build directly) and decompose it into a structured scope document.
|
|
28215
28490
|
|
|
28216
28491
|
A scope document must:
|
|
@@ -28267,14 +28542,14 @@ async function applyScopeBrief(adapter2, input) {
|
|
|
28267
28542
|
}
|
|
28268
28543
|
const slug = input.taskId.replace(/[^a-z0-9-]/g, "-").toLowerCase();
|
|
28269
28544
|
const relPath = `docs/scopes/${slug}.md`;
|
|
28270
|
-
const absPath =
|
|
28545
|
+
const absPath = join18(input.projectRoot, relPath);
|
|
28271
28546
|
const docBody = addFrontmatter(docContent, task, input.cycleNumber);
|
|
28272
28547
|
const collector = new FileWriteCollector();
|
|
28273
28548
|
if (input.adapterType === "proxy") {
|
|
28274
28549
|
collector.add({ path: relPath, content: docBody, mode: "overwrite" });
|
|
28275
28550
|
} else {
|
|
28276
|
-
|
|
28277
|
-
|
|
28551
|
+
mkdirSync4(dirname4(absPath), { recursive: true });
|
|
28552
|
+
writeFileSync6(absPath, docBody, "utf-8");
|
|
28278
28553
|
}
|
|
28279
28554
|
const taskCount = countSubTasks(docContent);
|
|
28280
28555
|
const summary = buildSummary(task, taskCount);
|
|
@@ -28445,7 +28720,7 @@ function readLlmResponse(args) {
|
|
|
28445
28720
|
if (statSync7(filePath).size > MAX_RESPONSE_FILE_BYTES) {
|
|
28446
28721
|
throw new Error(`llm_response_file exceeds ${MAX_RESPONSE_FILE_BYTES} bytes`);
|
|
28447
28722
|
}
|
|
28448
|
-
const body =
|
|
28723
|
+
const body = readFileSync12(filePath, "utf-8");
|
|
28449
28724
|
return body.trim() ? body : null;
|
|
28450
28725
|
} catch (err) {
|
|
28451
28726
|
throw new Error(`could not read llm_response_file: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -28640,6 +28915,7 @@ async function handleDiscoveredIssueResolve(adapter2, args) {
|
|
|
28640
28915
|
import path6 from "path";
|
|
28641
28916
|
|
|
28642
28917
|
// src/services/entitlements.ts
|
|
28918
|
+
import { evaluateContributorGate } from "@papi-ai/shared";
|
|
28643
28919
|
var FREE_PROJECT_CAP = 3;
|
|
28644
28920
|
var PAID_TIERS = /* @__PURE__ */ new Set(["pro", "team"]);
|
|
28645
28921
|
var PRICING_URL = "https://getpapi.ai/pricing";
|
|
@@ -28672,11 +28948,9 @@ async function enforceProjectCap(adapter2, target) {
|
|
|
28672
28948
|
if (projects.length >= FREE_PROJECT_CAP) return projectCapMessage(projects.length);
|
|
28673
28949
|
return null;
|
|
28674
28950
|
}
|
|
28675
|
-
async function
|
|
28951
|
+
async function resolveContributorUpsell(adapter2) {
|
|
28676
28952
|
const tier = await resolveTier(adapter2);
|
|
28677
|
-
|
|
28678
|
-
if (tier === "team") return null;
|
|
28679
|
-
return contributorTeamMessage(tier);
|
|
28953
|
+
return evaluateContributorGate(tier).upsell ?? null;
|
|
28680
28954
|
}
|
|
28681
28955
|
function projectCapMessage(currentCount) {
|
|
28682
28956
|
return `**You're on the Free plan (${currentCount} of ${FREE_PROJECT_CAP} projects).**
|
|
@@ -28685,11 +28959,6 @@ Free covers up to ${FREE_PROJECT_CAP} projects. To run more, upgrade to Pro for
|
|
|
28685
28959
|
|
|
28686
28960
|
Your existing projects are untouched, and you can keep working in any of them.`;
|
|
28687
28961
|
}
|
|
28688
|
-
function contributorTeamMessage(tier) {
|
|
28689
|
-
return `**Adding people to a project is a Team feature.**
|
|
28690
|
-
|
|
28691
|
-
You're on the ${tier} plan. Shared projects, roles, and the Quality Gate come with Team. Read-only viewer seats are free, so you only pay for who is actually building: ${PRICING_URL}`;
|
|
28692
|
-
}
|
|
28693
28962
|
|
|
28694
28963
|
// src/tools/project.ts
|
|
28695
28964
|
function workspacePapiDir(config2) {
|
|
@@ -28883,17 +29152,19 @@ function requireEmail(args) {
|
|
|
28883
29152
|
async function handleContributorAdd(adapter2, config2, args) {
|
|
28884
29153
|
const denied = await denyUnlessOwner(adapter2, config2);
|
|
28885
29154
|
if (denied) return errorResponse(denied);
|
|
28886
|
-
const tierDenied = await enforceContributorGate(adapter2);
|
|
28887
|
-
if (tierDenied) return errorResponse(tierDenied);
|
|
28888
29155
|
const email = requireEmail(args);
|
|
28889
29156
|
if (!email) return errorResponse('A valid email is required. Example: contributor_add email="wes@example.com"');
|
|
28890
29157
|
try {
|
|
28891
29158
|
const entry = await adapter2.addContributorByEmail(email);
|
|
28892
29159
|
const name = entry.displayName ? ` (${entry.displayName})` : "";
|
|
29160
|
+
const upsell = await resolveContributorUpsell(adapter2);
|
|
29161
|
+
const upsellSuffix = upsell ? `
|
|
29162
|
+
|
|
29163
|
+
${upsell}` : "";
|
|
28893
29164
|
return textResponse(
|
|
28894
29165
|
`\u2705 Added **${entry.email ?? email}**${name} as a contributor.
|
|
28895
29166
|
|
|
28896
|
-
They now have contributors-tier visibility on this project. Roles and invites land with MU-2 \u2014 today membership is the whole model.`
|
|
29167
|
+
They now have contributors-tier visibility on this project. Roles and invites land with MU-2 \u2014 today membership is the whole model.` + upsellSuffix
|
|
28897
29168
|
);
|
|
28898
29169
|
} catch (err) {
|
|
28899
29170
|
return errorResponse(err instanceof Error ? err.message : String(err));
|
|
@@ -29158,19 +29429,19 @@ Its build reports, comments, and history moved with it; the cycle assignment was
|
|
|
29158
29429
|
|
|
29159
29430
|
// src/services/harness-inventory.ts
|
|
29160
29431
|
import { readdir as readdir3, readFile as readFile8, stat as stat3 } from "fs/promises";
|
|
29161
|
-
import { join as
|
|
29162
|
-
import { createHash as
|
|
29432
|
+
import { join as join19 } from "path";
|
|
29433
|
+
import { createHash as createHash5 } from "crypto";
|
|
29163
29434
|
var RECOMMENDED_HOOKS = ["stop-release-check.sh", "claude-md-size-guard.sh"];
|
|
29164
29435
|
async function computeFingerprint(root) {
|
|
29165
29436
|
const parts = [];
|
|
29166
29437
|
for (const sub of [".claude/skills", ".claude/agents", ".claude/hooks"]) {
|
|
29167
|
-
const dir =
|
|
29438
|
+
const dir = join19(root, sub);
|
|
29168
29439
|
try {
|
|
29169
29440
|
const names = (await readdir3(dir)).sort((a, b2) => a.localeCompare(b2));
|
|
29170
29441
|
for (const name of names) {
|
|
29171
29442
|
let mtime = "";
|
|
29172
29443
|
try {
|
|
29173
|
-
mtime = String(Math.floor((await stat3(
|
|
29444
|
+
mtime = String(Math.floor((await stat3(join19(dir, name))).mtimeMs));
|
|
29174
29445
|
} catch {
|
|
29175
29446
|
}
|
|
29176
29447
|
parts.push(`${sub}/${name}:${mtime}`);
|
|
@@ -29185,11 +29456,11 @@ async function computeFingerprint(root) {
|
|
|
29185
29456
|
} catch {
|
|
29186
29457
|
parts.push("manifest:none");
|
|
29187
29458
|
}
|
|
29188
|
-
return
|
|
29459
|
+
return createHash5("sha256").update(parts.join("|")).digest("hex").slice(0, 16);
|
|
29189
29460
|
}
|
|
29190
29461
|
async function readSkillDescription(skillDir) {
|
|
29191
29462
|
try {
|
|
29192
|
-
const content = await readFile8(
|
|
29463
|
+
const content = await readFile8(join19(skillDir, "SKILL.md"), "utf-8");
|
|
29193
29464
|
const fm = content.match(/^---\n([\s\S]*?)\n---/);
|
|
29194
29465
|
if (!fm) return void 0;
|
|
29195
29466
|
const desc = fm[1].match(/^description:\s*[>|]?\s*\n?([\s\S]*?)(?=\n\w+:|\n---|$)/m);
|
|
@@ -29209,17 +29480,17 @@ async function scanInventory(root, toolDefs) {
|
|
|
29209
29480
|
version = loadManifest().packageVersion;
|
|
29210
29481
|
} catch {
|
|
29211
29482
|
}
|
|
29212
|
-
const skillsDir =
|
|
29483
|
+
const skillsDir = join19(root, ".claude", "skills");
|
|
29213
29484
|
try {
|
|
29214
29485
|
const dirents = await readdir3(skillsDir, { withFileTypes: true });
|
|
29215
29486
|
for (const d of dirents.filter((e) => e.isDirectory())) {
|
|
29216
29487
|
entries.push({
|
|
29217
29488
|
kind: "skill",
|
|
29218
29489
|
name: d.name,
|
|
29219
|
-
description: await readSkillDescription(
|
|
29490
|
+
description: await readSkillDescription(join19(skillsDir, d.name)),
|
|
29220
29491
|
version,
|
|
29221
29492
|
status: stale.has(d.name) ? "stale_fork" : "ok",
|
|
29222
|
-
path:
|
|
29493
|
+
path: join19(".claude", "skills", d.name)
|
|
29223
29494
|
});
|
|
29224
29495
|
}
|
|
29225
29496
|
} catch {
|
|
@@ -29235,9 +29506,9 @@ async function scanInventory(root, toolDefs) {
|
|
|
29235
29506
|
}
|
|
29236
29507
|
const present = /* @__PURE__ */ new Set();
|
|
29237
29508
|
try {
|
|
29238
|
-
for (const f of (await readdir3(
|
|
29509
|
+
for (const f of (await readdir3(join19(root, ".claude", "hooks"))).filter((n) => n.endsWith(".sh"))) {
|
|
29239
29510
|
present.add(f);
|
|
29240
|
-
entries.push({ kind: "hook", name: f, status: "ok", path:
|
|
29511
|
+
entries.push({ kind: "hook", name: f, status: "ok", path: join19(".claude", "hooks", f) });
|
|
29241
29512
|
}
|
|
29242
29513
|
} catch {
|
|
29243
29514
|
}
|
|
@@ -29636,6 +29907,7 @@ var PAPI_TOOLS = [
|
|
|
29636
29907
|
buildCancelTool,
|
|
29637
29908
|
ideaTool,
|
|
29638
29909
|
bugTool,
|
|
29910
|
+
bugListTool,
|
|
29639
29911
|
adHocTool,
|
|
29640
29912
|
boardReconcileTool,
|
|
29641
29913
|
releaseTool,
|
|
@@ -29651,6 +29923,8 @@ var PAPI_TOOLS = [
|
|
|
29651
29923
|
docSearchTool,
|
|
29652
29924
|
docScanTool,
|
|
29653
29925
|
docActionPromoteTool,
|
|
29926
|
+
docDeleteTool,
|
|
29927
|
+
docReorderTool,
|
|
29654
29928
|
getSiblingAdsTool,
|
|
29655
29929
|
handoffGenerateTool,
|
|
29656
29930
|
scopeBriefTool,
|
|
@@ -29677,7 +29951,7 @@ function createServer(adapter2, config2) {
|
|
|
29677
29951
|
const __pkgDir = dirname5(__pkgFilename);
|
|
29678
29952
|
let serverVersion = "unknown";
|
|
29679
29953
|
try {
|
|
29680
|
-
const pkg = JSON.parse(
|
|
29954
|
+
const pkg = JSON.parse(readFileSync13(join20(__pkgDir, "..", "package.json"), "utf-8"));
|
|
29681
29955
|
serverVersion = pkg.version ?? "unknown";
|
|
29682
29956
|
} catch {
|
|
29683
29957
|
}
|
|
@@ -29695,7 +29969,7 @@ function createServer(adapter2, config2) {
|
|
|
29695
29969
|
}
|
|
29696
29970
|
const __filename = fileURLToPath3(import.meta.url);
|
|
29697
29971
|
const __dirname2 = dirname5(__filename);
|
|
29698
|
-
const skillsDir =
|
|
29972
|
+
const skillsDir = join20(__dirname2, "..", "skills");
|
|
29699
29973
|
function parseSkillFrontmatter(content) {
|
|
29700
29974
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
29701
29975
|
if (!match) return null;
|
|
@@ -29713,7 +29987,7 @@ function createServer(adapter2, config2) {
|
|
|
29713
29987
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
29714
29988
|
const prompts = [];
|
|
29715
29989
|
for (const file of mdFiles) {
|
|
29716
|
-
const content = await readFile9(
|
|
29990
|
+
const content = await readFile9(join20(skillsDir, file), "utf-8");
|
|
29717
29991
|
const meta = parseSkillFrontmatter(content);
|
|
29718
29992
|
if (meta) {
|
|
29719
29993
|
prompts.push({ name: meta.name, description: meta.description });
|
|
@@ -29729,7 +30003,7 @@ function createServer(adapter2, config2) {
|
|
|
29729
30003
|
try {
|
|
29730
30004
|
const files = await readdir4(skillsDir);
|
|
29731
30005
|
for (const file of files.filter((f) => f.endsWith(".md"))) {
|
|
29732
|
-
const content = await readFile9(
|
|
30006
|
+
const content = await readFile9(join20(skillsDir, file), "utf-8");
|
|
29733
30007
|
const meta = parseSkillFrontmatter(content);
|
|
29734
30008
|
if (meta?.name === name) {
|
|
29735
30009
|
const body = content.replace(/^---\n[\s\S]*?\n---\n*/, "");
|
|
@@ -29816,6 +30090,8 @@ function createServer(adapter2, config2) {
|
|
|
29816
30090
|
return handleIdea(adapter2, config2, safeArgs);
|
|
29817
30091
|
case "bug":
|
|
29818
30092
|
return handleBug(adapter2, config2, safeArgs);
|
|
30093
|
+
case "bug_list":
|
|
30094
|
+
return handleBugList(adapter2, config2);
|
|
29819
30095
|
case "ad_hoc":
|
|
29820
30096
|
return handleAdHoc(adapter2, config2, safeArgs);
|
|
29821
30097
|
case "board_reconcile":
|
|
@@ -29847,6 +30123,10 @@ function createServer(adapter2, config2) {
|
|
|
29847
30123
|
return handleDocScan(adapter2, config2, safeArgs);
|
|
29848
30124
|
case "doc_action_promote":
|
|
29849
30125
|
return handleDocActionPromote(adapter2, safeArgs);
|
|
30126
|
+
case "doc_delete":
|
|
30127
|
+
return handleDocDelete(adapter2, config2, safeArgs);
|
|
30128
|
+
case "doc_reorder":
|
|
30129
|
+
return handleDocReorder(adapter2, safeArgs);
|
|
29850
30130
|
case "get_sibling_ads":
|
|
29851
30131
|
return handleGetSiblingAds(adapter2, safeArgs);
|
|
29852
30132
|
case "handoff_generate":
|
|
@@ -30423,7 +30703,7 @@ async function dispatchRequest(args) {
|
|
|
30423
30703
|
var __dirname = dirname6(fileURLToPath4(import.meta.url));
|
|
30424
30704
|
var pkgVersion = "unknown";
|
|
30425
30705
|
try {
|
|
30426
|
-
const pkg = JSON.parse(
|
|
30706
|
+
const pkg = JSON.parse(readFileSync18(join25(__dirname, "..", "package.json"), "utf-8"));
|
|
30427
30707
|
pkgVersion = pkg.version;
|
|
30428
30708
|
} catch {
|
|
30429
30709
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papi-ai/server",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.67",
|
|
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.getpapi/papi",
|