@papi-ai/server 0.7.66 → 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.
Files changed (2) hide show
  1. package/dist/index.js +193 -88
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4340,9 +4340,9 @@ __export(doctor_exports, {
4340
4340
  __testing: () => __testing,
4341
4341
  runDoctor: () => runDoctor
4342
4342
  });
4343
- import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
4343
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
4344
4344
  import { homedir as homedir4 } from "os";
4345
- import { join as join20 } from "path";
4345
+ import { join as join21 } from "path";
4346
4346
  function redact(name, value) {
4347
4347
  if (!value) return "(empty)";
4348
4348
  if (SECRET_VARS.has(name)) {
@@ -4353,14 +4353,14 @@ function redact(name, value) {
4353
4353
  }
4354
4354
  function findMcpJson() {
4355
4355
  const candidates = [
4356
- join20(process.cwd(), ".mcp.json"),
4357
- join20(homedir4(), ".claude", ".mcp.json"),
4358
- join20(homedir4(), ".mcp.json")
4356
+ join21(process.cwd(), ".mcp.json"),
4357
+ join21(homedir4(), ".claude", ".mcp.json"),
4358
+ join21(homedir4(), ".mcp.json")
4359
4359
  ];
4360
4360
  for (const path7 of candidates) {
4361
- if (!existsSync11(path7)) continue;
4361
+ if (!existsSync12(path7)) continue;
4362
4362
  try {
4363
- const raw = readFileSync13(path7, "utf-8");
4363
+ const raw = readFileSync14(path7, "utf-8");
4364
4364
  const parsed = JSON.parse(raw);
4365
4365
  const papiEntry = parsed.papi ?? parsed.mcpServers?.papi;
4366
4366
  if (!papiEntry) continue;
@@ -4638,17 +4638,17 @@ __export(reset_exports, {
4638
4638
  removePapiEntry: () => removePapiEntry,
4639
4639
  runReset: () => runReset
4640
4640
  });
4641
- import { existsSync as existsSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
4641
+ import { existsSync as existsSync13, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
4642
4642
  import { homedir as homedir5 } from "os";
4643
- import { join as join21 } from "path";
4643
+ import { join as join22 } from "path";
4644
4644
  import { createInterface } from "readline/promises";
4645
4645
  function findResetTarget() {
4646
4646
  for (const path7 of CANDIDATE_PATHS()) {
4647
- if (!existsSync12(path7)) continue;
4647
+ if (!existsSync13(path7)) continue;
4648
4648
  let raw;
4649
4649
  let parsed;
4650
4650
  try {
4651
- raw = readFileSync14(path7, "utf-8");
4651
+ raw = readFileSync15(path7, "utf-8");
4652
4652
  parsed = JSON.parse(raw);
4653
4653
  } catch {
4654
4654
  continue;
@@ -4719,7 +4719,7 @@ async function runReset(args = []) {
4719
4719
  }
4720
4720
  }
4721
4721
  try {
4722
- writeFileSync6(target.path, removePapiEntry(target), "utf-8");
4722
+ writeFileSync7(target.path, removePapiEntry(target), "utf-8");
4723
4723
  process.stdout.write(`
4724
4724
  \u2713 Removed papi entry from ${target.path}
4725
4725
  `);
@@ -4736,9 +4736,9 @@ var init_reset = __esm({
4736
4736
  "src/cli/reset.ts"() {
4737
4737
  "use strict";
4738
4738
  CANDIDATE_PATHS = () => [
4739
- join21(process.cwd(), ".mcp.json"),
4740
- join21(homedir5(), ".claude", ".mcp.json"),
4741
- join21(homedir5(), ".mcp.json")
4739
+ join22(process.cwd(), ".mcp.json"),
4740
+ join22(homedir5(), ".claude", ".mcp.json"),
4741
+ join22(homedir5(), ".mcp.json")
4742
4742
  ];
4743
4743
  }
4744
4744
  });
@@ -4749,9 +4749,9 @@ __export(audit_exports, {
4749
4749
  __testing: () => __testing2,
4750
4750
  runAudit: () => runAudit
4751
4751
  });
4752
- import { existsSync as existsSync13, readFileSync as readFileSync15, readdirSync as readdirSync7 } from "fs";
4752
+ import { existsSync as existsSync14, readFileSync as readFileSync16, readdirSync as readdirSync7 } from "fs";
4753
4753
  import { homedir as homedir6 } from "os";
4754
- import { join as join22 } from "path";
4754
+ import { join as join23 } from "path";
4755
4755
  function safeListDirs(dir) {
4756
4756
  try {
4757
4757
  return readdirSync7(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort((a, b2) => a.localeCompare(b2));
@@ -4767,10 +4767,10 @@ function safeListFiles(dir, ext) {
4767
4767
  }
4768
4768
  }
4769
4769
  function readMcp(projectPath) {
4770
- const path7 = join22(projectPath, ".mcp.json");
4771
- if (!existsSync13(path7)) return { servers: [] };
4770
+ const path7 = join23(projectPath, ".mcp.json");
4771
+ if (!existsSync14(path7)) return { servers: [] };
4772
4772
  try {
4773
- const parsed = JSON.parse(readFileSync15(path7, "utf-8"));
4773
+ const parsed = JSON.parse(readFileSync16(path7, "utf-8"));
4774
4774
  const mcpServers = parsed.mcpServers ?? {};
4775
4775
  const servers = Object.keys(mcpServers);
4776
4776
  if (parsed.papi && !servers.includes("papi")) servers.push("papi");
@@ -4802,18 +4802,18 @@ function auditProjectSync(projectPath, name) {
4802
4802
  path: projectPath,
4803
4803
  papiProjectId,
4804
4804
  mcpServers: servers,
4805
- skills: safeListDirs(join22(projectPath, ".claude", "skills")),
4806
- agentSkills: safeListDirs(join22(projectPath, ".agents", "skills")),
4807
- agents: safeListFiles(join22(projectPath, ".claude", "agents"), ".md"),
4808
- hooks: safeListFiles(join22(projectPath, ".claude", "hooks"), ".sh")
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")
4809
4809
  };
4810
4810
  }
4811
4811
  function discoverProjects() {
4812
4812
  const out = [];
4813
4813
  for (const root of PROJECT_ROOTS) {
4814
4814
  for (const name of safeListDirs(root)) {
4815
- const path7 = join22(root, name);
4816
- if (existsSync13(join22(path7, ".mcp.json")) || existsSync13(join22(path7, ".claude"))) {
4815
+ const path7 = join23(root, name);
4816
+ if (existsSync14(join23(path7, ".mcp.json")) || existsSync14(join23(path7, ".claude"))) {
4817
4817
  out.push({ name, path: path7 });
4818
4818
  }
4819
4819
  }
@@ -4824,9 +4824,9 @@ function readGlobalSkills() {
4824
4824
  return safeListDirs(GLOBAL_SKILLS_DIR);
4825
4825
  }
4826
4826
  function readGlobalMcpServers() {
4827
- if (!existsSync13(GLOBAL_CLAUDE_JSON)) return [];
4827
+ if (!existsSync14(GLOBAL_CLAUDE_JSON)) return [];
4828
4828
  try {
4829
- const parsed = JSON.parse(readFileSync15(GLOBAL_CLAUDE_JSON, "utf-8"));
4829
+ const parsed = JSON.parse(readFileSync16(GLOBAL_CLAUDE_JSON, "utf-8"));
4830
4830
  const servers = parsed.mcpServers ?? {};
4831
4831
  return Object.keys(servers).sort((a, b2) => a.localeCompare(b2));
4832
4832
  } catch {
@@ -4978,9 +4978,9 @@ var PROJECT_ROOTS, GLOBAL_SKILLS_DIR, GLOBAL_CLAUDE_JSON, IDLE_WINDOW_DAYS, GLOB
4978
4978
  var init_audit = __esm({
4979
4979
  "src/cli/audit.ts"() {
4980
4980
  "use strict";
4981
- PROJECT_ROOTS = [join22(homedir6(), "Ai-App-Projects"), join22(homedir6(), "android-projects")];
4982
- GLOBAL_SKILLS_DIR = join22(homedir6(), ".claude", "skills");
4983
- GLOBAL_CLAUDE_JSON = join22(homedir6(), ".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");
4984
4984
  IDLE_WINDOW_DAYS = 30;
4985
4985
  GLOBALIZE_THRESHOLD = 3;
4986
4986
  __testing2 = { readMcp, computeFlags, formatReport: formatReport2, discoverProjects, auditProjectSync };
@@ -4992,8 +4992,8 @@ var setup_exports = {};
4992
4992
  __export(setup_exports, {
4993
4993
  runSetup: () => runSetup
4994
4994
  });
4995
- import { existsSync as existsSync14, readFileSync as readFileSync16, writeFileSync as writeFileSync7, chmodSync as chmodSync2, statSync as statSync8 } from "fs";
4996
- import { join as join23 } from "path";
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";
4997
4997
  function baseUrl() {
4998
4998
  const fromEnv = process.env["PAPI_HOST"] ?? process.env["PAPI_BASE_URL"];
4999
4999
  if (fromEnv) return fromEnv.replace(/\/$/, "");
@@ -5025,11 +5025,11 @@ function sleep(ms) {
5025
5025
  return new Promise((resolve3) => setTimeout(resolve3, ms));
5026
5026
  }
5027
5027
  function writeMcpJson(opts) {
5028
- const path7 = join23(process.cwd(), ".mcp.json");
5028
+ const path7 = join24(process.cwd(), ".mcp.json");
5029
5029
  let parsed = {};
5030
- if (existsSync14(path7)) {
5030
+ if (existsSync15(path7)) {
5031
5031
  try {
5032
- parsed = JSON.parse(readFileSync16(path7, "utf-8"));
5032
+ parsed = JSON.parse(readFileSync17(path7, "utf-8"));
5033
5033
  } catch {
5034
5034
  throw new Error(`.mcp.json at ${path7} is not valid JSON. Fix it or remove it before re-running setup.`);
5035
5035
  }
@@ -5050,7 +5050,7 @@ function writeMcpJson(opts) {
5050
5050
  }
5051
5051
  mcpServers.papi = papiEntry;
5052
5052
  parsed.mcpServers = mcpServers;
5053
- writeFileSync7(path7, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
5053
+ writeFileSync8(path7, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
5054
5054
  try {
5055
5055
  const mode = statSync8(path7).mode & 511;
5056
5056
  if (mode !== 384) chmodSync2(path7, 384);
@@ -5160,8 +5160,8 @@ var init_setup = __esm({
5160
5160
  });
5161
5161
 
5162
5162
  // src/index.ts
5163
- import { readFileSync as readFileSync17 } from "fs";
5164
- import { dirname as dirname6, join as join24, basename as basename2 } from "path";
5163
+ import { readFileSync as readFileSync18 } from "fs";
5164
+ import { dirname as dirname6, join as join25, basename as basename2 } from "path";
5165
5165
  import { fileURLToPath as fileURLToPath4 } from "url";
5166
5166
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5167
5167
  import { Server as Server2 } from "@modelcontextprotocol/sdk/server/index.js";
@@ -8199,9 +8199,9 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
8199
8199
  }
8200
8200
 
8201
8201
  // src/server.ts
8202
- import { readFileSync as readFileSync12 } from "fs";
8202
+ import { readFileSync as readFileSync13 } from "fs";
8203
8203
  import { access as access4, readdir as readdir4, readFile as readFile9 } from "fs/promises";
8204
- import { join as join19, dirname as dirname5 } from "path";
8204
+ import { join as join20, dirname as dirname5 } from "path";
8205
8205
  import { fileURLToPath as fileURLToPath3 } from "url";
8206
8206
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8207
8207
  import {
@@ -22435,6 +22435,97 @@ async function handleDocReorder(adapter2, args) {
22435
22435
 
22436
22436
  // src/tools/build.ts
22437
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
22438
22529
  var buildListTool = {
22439
22530
  name: "build_list",
22440
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.",
@@ -22772,10 +22863,23 @@ async function handleBuildExecute(adapter2, config2, args, clientName) {
22772
22863
  if (scopeTask) {
22773
22864
  tracker.setStreamScope({ taskId: scopeTask.displayId ?? scopeTask.id, cycle: scopeTask.cycle ?? null });
22774
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
+ }
22775
22871
  await tracker.recordStep("started");
22776
22872
  const result = await startBuild(adapter2, config2, taskId, { light }, clientName);
22777
22873
  tracker.setStreamScope({ taskId: result.task.displayId ?? result.task.id, cycle: result.task.cycle ?? null });
22778
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
+ });
22779
22883
  tracker.mark("start_decorate_handoff");
22780
22884
  const branchInfo = result.branchLines.length > 0 ? result.branchLines.map((l) => `> ${l}`).join("\n") + "\n\n" : "";
22781
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") : "";
@@ -22856,7 +22960,7 @@ If this build fixes any of these, pass their UUIDs in \`fixed_issues\` on comple
22856
22960
  ) ?? "";
22857
22961
  const gestaltNote = buildGestaltPreBuildDirective(caps) ?? "";
22858
22962
  const buildDisciplineSection = buildPapiMetaFramingDirective(caps, buildDisciplineNote) ?? "";
22859
- return textResponse(header + serializeBuildHandoff(result.task.buildHandoff) + modelNote + gestaltNote + adSection + moduleInstructions + moduleContext + dogfoodSection + openIssuesSection + verificationNote + buildDisciplineSection + chainInstruction + phaseNote + filesToWriteSection);
22963
+ return textResponse(resumeNote + header + serializeBuildHandoff(result.task.buildHandoff) + modelNote + gestaltNote + adSection + moduleInstructions + moduleContext + dogfoodSection + openIssuesSection + verificationNote + buildDisciplineSection + chainInstruction + phaseNote + filesToWriteSection);
22860
22964
  } catch (err) {
22861
22965
  if (isNoHandoffError(err)) {
22862
22966
  const lines = [
@@ -22994,6 +23098,7 @@ Your report was NOT discarded and the task is NOT yet Done \u2014 re-send with \
22994
23098
  preview
22995
23099
  }, { light }, clientName);
22996
23100
  tracker.mark("complete_format");
23101
+ clearBuildCheckpoint({ cwd: config2.projectRoot, taskId });
22997
23102
  tracker.setStreamScope({ taskId: result.task.displayId ?? result.task.id, cycle: result.cycleNumber });
22998
23103
  await tracker.recordStep("report_written");
22999
23104
  if ((result.autoTriagedCount ?? 0) > 0) {
@@ -23877,13 +23982,13 @@ _To correct: board_edit ${result.task.id} with updated fields._`
23877
23982
  init_git();
23878
23983
 
23879
23984
  // src/services/reconcile.ts
23880
- import { readFileSync as readFileSync8 } from "fs";
23881
- import { join as join13 } from "path";
23985
+ import { readFileSync as readFileSync9 } from "fs";
23986
+ import { join as join14 } from "path";
23882
23987
  function loadDocsIndex(projectRoot) {
23883
23988
  if (!hasLocalWorkspace()) return "";
23884
23989
  try {
23885
- const indexPath = join13(projectRoot, "docs", "INDEX.md");
23886
- const raw = readFileSync8(indexPath, "utf8");
23990
+ const indexPath = join14(projectRoot, "docs", "INDEX.md");
23991
+ const raw = readFileSync9(indexPath, "utf8");
23887
23992
  const rows = raw.split("\n").filter((l) => l.startsWith("| ["));
23888
23993
  if (rows.length === 0) return "";
23889
23994
  const entries = rows.map((row) => {
@@ -24458,8 +24563,8 @@ Produce your analysis and structured output above. Present Part 1 to the user an
24458
24563
  }
24459
24564
 
24460
24565
  // src/tools/review.ts
24461
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
24462
- import { join as join14 } from "path";
24566
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
24567
+ import { join as join15 } from "path";
24463
24568
  init_git();
24464
24569
 
24465
24570
  // src/services/review.ts
@@ -24688,11 +24793,11 @@ ${task.buildReport}` : "### Build Report\n(none recorded)";
24688
24793
  ${diff}
24689
24794
  \`\`\`` : "### Branch diff vs base\n(no diff resolved \u2014 not a git repo, no base ref, or no committed changes)";
24690
24795
  let projectContext = "";
24691
- const ctxPath = join14(config2.projectRoot, ".agents", "papi-context.md");
24692
- if (existsSync9(ctxPath)) {
24796
+ const ctxPath = join15(config2.projectRoot, ".agents", "papi-context.md");
24797
+ if (existsSync10(ctxPath)) {
24693
24798
  try {
24694
24799
  projectContext = `### Project context (.agents/papi-context.md)
24695
- ${readFileSync9(ctxPath, "utf-8")}
24800
+ ${readFileSync10(ctxPath, "utf-8")}
24696
24801
 
24697
24802
  `;
24698
24803
  } catch {
@@ -24852,8 +24957,8 @@ function mergeAfterAccept(config2, taskId) {
24852
24957
  };
24853
24958
  }
24854
24959
  const details = [];
24855
- const papiDir = join14(config2.projectRoot, ".papi");
24856
- if (existsSync9(papiDir)) {
24960
+ const papiDir = join15(config2.projectRoot, ".papi");
24961
+ if (existsSync10(papiDir)) {
24857
24962
  try {
24858
24963
  const commitResult = stageDirAndCommit(
24859
24964
  config2.projectRoot,
@@ -26435,7 +26540,7 @@ function formatDeferredGateSection(sweep) {
26435
26540
 
26436
26541
  // src/tools/agent-list.ts
26437
26542
  import { readdir as readdir2, readFile as readFile7 } from "fs/promises";
26438
- import { join as join15 } from "path";
26543
+ import { join as join16 } from "path";
26439
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.";
26440
26545
  function parseAgentFrontmatter(content) {
26441
26546
  const match = content.match(/^---\n([\s\S]*?)\n---/);
@@ -26447,7 +26552,7 @@ function parseAgentFrontmatter(content) {
26447
26552
  return { name: nameMatch?.[1].trim(), description };
26448
26553
  }
26449
26554
  async function listAgents(projectRoot) {
26450
- const agentsDir = join15(projectRoot, ".claude", "agents");
26555
+ const agentsDir = join16(projectRoot, ".claude", "agents");
26451
26556
  let files;
26452
26557
  try {
26453
26558
  files = await readdir2(agentsDir);
@@ -26458,7 +26563,7 @@ async function listAgents(projectRoot) {
26458
26563
  for (const file of files.filter((f) => f.endsWith(".md"))) {
26459
26564
  let content;
26460
26565
  try {
26461
- content = await readFile7(join15(agentsDir, file), "utf-8");
26566
+ content = await readFile7(join16(agentsDir, file), "utf-8");
26462
26567
  } catch {
26463
26568
  continue;
26464
26569
  }
@@ -26466,7 +26571,7 @@ async function listAgents(projectRoot) {
26466
26571
  agents.push({
26467
26572
  name: meta?.name ?? file.replace(/\.md$/, ""),
26468
26573
  description: meta?.description ?? "",
26469
- path: join15(".claude", "agents", file)
26574
+ path: join16(".claude", "agents", file)
26470
26575
  });
26471
26576
  }
26472
26577
  agents.sort((a, b2) => a.name.localeCompare(b2.name));
@@ -26607,8 +26712,8 @@ async function verifyProject(adapter2) {
26607
26712
  // src/tools/orient.ts
26608
26713
  import { execFile as execFile2 } from "child_process";
26609
26714
  import { promisify as promisify2 } from "util";
26610
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, existsSync as existsSync10 } from "fs";
26611
- import { join as join16 } from "path";
26715
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync5, existsSync as existsSync11 } from "fs";
26716
+ import { join as join17 } from "path";
26612
26717
  var execFileAsync2 = promisify2(execFile2);
26613
26718
  var GIT_DEPENDENT_ENVS = /* @__PURE__ */ new Set(["hosted", "api"]);
26614
26719
  var VALID_ENVS = /* @__PURE__ */ new Set(["local-cli", "hosted", "api", "unknown"]);
@@ -26967,8 +27072,8 @@ async function getLatestGitTag(projectRoot) {
26967
27072
  }
26968
27073
  async function checkNpmVersionDrift() {
26969
27074
  try {
26970
- const pkgPath = join16(new URL(".", import.meta.url).pathname, "..", "..", "package.json");
26971
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
27075
+ const pkgPath = join17(new URL(".", import.meta.url).pathname, "..", "..", "package.json");
27076
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
26972
27077
  const localVersion = pkg.version;
26973
27078
  const packageName = pkg.name;
26974
27079
  const { stdout } = await execFileAsync2("npm", ["view", packageName, "version"], {
@@ -27642,9 +27747,9 @@ function enrichClaudeMd(projectRoot, cycleNumber, adapterType, collector, client
27642
27747
 
27643
27748
  \u{1F4DD} **CLAUDE.md enriched** \u2014 added ${tierNames2.join(" + ")} guidance for cycle ${cycleNumber}+ projects.`;
27644
27749
  }
27645
- const claudeMdPath = join16(projectRoot, "CLAUDE.md");
27646
- if (!existsSync10(claudeMdPath)) return "";
27647
- const content = readFileSync10(claudeMdPath, "utf-8");
27750
+ const claudeMdPath = join17(projectRoot, "CLAUDE.md");
27751
+ if (!existsSync11(claudeMdPath)) return "";
27752
+ const content = readFileSync11(claudeMdPath, "utf-8");
27648
27753
  const additions = [];
27649
27754
  if (cycleNumber >= 6 && !content.includes(CLAUDE_MD_ENRICHMENT_SENTINEL_T1)) {
27650
27755
  additions.push(dedupeEnrichmentBlob(content, CLAUDE_MD_TIER_1));
@@ -27653,7 +27758,7 @@ function enrichClaudeMd(projectRoot, cycleNumber, adapterType, collector, client
27653
27758
  additions.push(dedupeEnrichmentBlob(content, CLAUDE_MD_TIER_2));
27654
27759
  }
27655
27760
  if (additions.length === 0) return "";
27656
- writeFileSync4(claudeMdPath, content + additions.join(""), "utf-8");
27761
+ writeFileSync5(claudeMdPath, content + additions.join(""), "utf-8");
27657
27762
  const tierNames = [];
27658
27763
  if (additions.some((a) => a.includes(CLAUDE_MD_ENRICHMENT_SENTINEL_T1))) tierNames.push("Established (batch building, strategy reviews, AD lifecycle)");
27659
27764
  if (additions.some((a) => a.includes(CLAUDE_MD_ENRICHMENT_SENTINEL_T2))) tierNames.push("Mature (idea pipeline, doc registry, advanced patterns)");
@@ -28376,11 +28481,11 @@ ${result.userMessage}
28376
28481
  }
28377
28482
 
28378
28483
  // src/tools/scope-brief.ts
28379
- import { readFileSync as readFileSync11, statSync as statSync7 } from "fs";
28484
+ import { readFileSync as readFileSync12, statSync as statSync7 } from "fs";
28380
28485
 
28381
28486
  // src/services/scope-brief.ts
28382
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync3 } from "fs";
28383
- import { join as join17, dirname as dirname4 } from "path";
28487
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync4 } from "fs";
28488
+ import { join as join18, dirname as dirname4 } from "path";
28384
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.
28385
28490
 
28386
28491
  A scope document must:
@@ -28437,14 +28542,14 @@ async function applyScopeBrief(adapter2, input) {
28437
28542
  }
28438
28543
  const slug = input.taskId.replace(/[^a-z0-9-]/g, "-").toLowerCase();
28439
28544
  const relPath = `docs/scopes/${slug}.md`;
28440
- const absPath = join17(input.projectRoot, relPath);
28545
+ const absPath = join18(input.projectRoot, relPath);
28441
28546
  const docBody = addFrontmatter(docContent, task, input.cycleNumber);
28442
28547
  const collector = new FileWriteCollector();
28443
28548
  if (input.adapterType === "proxy") {
28444
28549
  collector.add({ path: relPath, content: docBody, mode: "overwrite" });
28445
28550
  } else {
28446
- mkdirSync3(dirname4(absPath), { recursive: true });
28447
- writeFileSync5(absPath, docBody, "utf-8");
28551
+ mkdirSync4(dirname4(absPath), { recursive: true });
28552
+ writeFileSync6(absPath, docBody, "utf-8");
28448
28553
  }
28449
28554
  const taskCount = countSubTasks(docContent);
28450
28555
  const summary = buildSummary(task, taskCount);
@@ -28615,7 +28720,7 @@ function readLlmResponse(args) {
28615
28720
  if (statSync7(filePath).size > MAX_RESPONSE_FILE_BYTES) {
28616
28721
  throw new Error(`llm_response_file exceeds ${MAX_RESPONSE_FILE_BYTES} bytes`);
28617
28722
  }
28618
- const body = readFileSync11(filePath, "utf-8");
28723
+ const body = readFileSync12(filePath, "utf-8");
28619
28724
  return body.trim() ? body : null;
28620
28725
  } catch (err) {
28621
28726
  throw new Error(`could not read llm_response_file: ${err instanceof Error ? err.message : String(err)}`);
@@ -29324,19 +29429,19 @@ Its build reports, comments, and history moved with it; the cycle assignment was
29324
29429
 
29325
29430
  // src/services/harness-inventory.ts
29326
29431
  import { readdir as readdir3, readFile as readFile8, stat as stat3 } from "fs/promises";
29327
- import { join as join18 } from "path";
29328
- import { createHash as createHash4 } from "crypto";
29432
+ import { join as join19 } from "path";
29433
+ import { createHash as createHash5 } from "crypto";
29329
29434
  var RECOMMENDED_HOOKS = ["stop-release-check.sh", "claude-md-size-guard.sh"];
29330
29435
  async function computeFingerprint(root) {
29331
29436
  const parts = [];
29332
29437
  for (const sub of [".claude/skills", ".claude/agents", ".claude/hooks"]) {
29333
- const dir = join18(root, sub);
29438
+ const dir = join19(root, sub);
29334
29439
  try {
29335
29440
  const names = (await readdir3(dir)).sort((a, b2) => a.localeCompare(b2));
29336
29441
  for (const name of names) {
29337
29442
  let mtime = "";
29338
29443
  try {
29339
- mtime = String(Math.floor((await stat3(join18(dir, name))).mtimeMs));
29444
+ mtime = String(Math.floor((await stat3(join19(dir, name))).mtimeMs));
29340
29445
  } catch {
29341
29446
  }
29342
29447
  parts.push(`${sub}/${name}:${mtime}`);
@@ -29351,11 +29456,11 @@ async function computeFingerprint(root) {
29351
29456
  } catch {
29352
29457
  parts.push("manifest:none");
29353
29458
  }
29354
- return createHash4("sha256").update(parts.join("|")).digest("hex").slice(0, 16);
29459
+ return createHash5("sha256").update(parts.join("|")).digest("hex").slice(0, 16);
29355
29460
  }
29356
29461
  async function readSkillDescription(skillDir) {
29357
29462
  try {
29358
- const content = await readFile8(join18(skillDir, "SKILL.md"), "utf-8");
29463
+ const content = await readFile8(join19(skillDir, "SKILL.md"), "utf-8");
29359
29464
  const fm = content.match(/^---\n([\s\S]*?)\n---/);
29360
29465
  if (!fm) return void 0;
29361
29466
  const desc = fm[1].match(/^description:\s*[>|]?\s*\n?([\s\S]*?)(?=\n\w+:|\n---|$)/m);
@@ -29375,17 +29480,17 @@ async function scanInventory(root, toolDefs) {
29375
29480
  version = loadManifest().packageVersion;
29376
29481
  } catch {
29377
29482
  }
29378
- const skillsDir = join18(root, ".claude", "skills");
29483
+ const skillsDir = join19(root, ".claude", "skills");
29379
29484
  try {
29380
29485
  const dirents = await readdir3(skillsDir, { withFileTypes: true });
29381
29486
  for (const d of dirents.filter((e) => e.isDirectory())) {
29382
29487
  entries.push({
29383
29488
  kind: "skill",
29384
29489
  name: d.name,
29385
- description: await readSkillDescription(join18(skillsDir, d.name)),
29490
+ description: await readSkillDescription(join19(skillsDir, d.name)),
29386
29491
  version,
29387
29492
  status: stale.has(d.name) ? "stale_fork" : "ok",
29388
- path: join18(".claude", "skills", d.name)
29493
+ path: join19(".claude", "skills", d.name)
29389
29494
  });
29390
29495
  }
29391
29496
  } catch {
@@ -29401,9 +29506,9 @@ async function scanInventory(root, toolDefs) {
29401
29506
  }
29402
29507
  const present = /* @__PURE__ */ new Set();
29403
29508
  try {
29404
- for (const f of (await readdir3(join18(root, ".claude", "hooks"))).filter((n) => n.endsWith(".sh"))) {
29509
+ for (const f of (await readdir3(join19(root, ".claude", "hooks"))).filter((n) => n.endsWith(".sh"))) {
29405
29510
  present.add(f);
29406
- entries.push({ kind: "hook", name: f, status: "ok", path: join18(".claude", "hooks", f) });
29511
+ entries.push({ kind: "hook", name: f, status: "ok", path: join19(".claude", "hooks", f) });
29407
29512
  }
29408
29513
  } catch {
29409
29514
  }
@@ -29846,7 +29951,7 @@ function createServer(adapter2, config2) {
29846
29951
  const __pkgDir = dirname5(__pkgFilename);
29847
29952
  let serverVersion = "unknown";
29848
29953
  try {
29849
- const pkg = JSON.parse(readFileSync12(join19(__pkgDir, "..", "package.json"), "utf-8"));
29954
+ const pkg = JSON.parse(readFileSync13(join20(__pkgDir, "..", "package.json"), "utf-8"));
29850
29955
  serverVersion = pkg.version ?? "unknown";
29851
29956
  } catch {
29852
29957
  }
@@ -29864,7 +29969,7 @@ function createServer(adapter2, config2) {
29864
29969
  }
29865
29970
  const __filename = fileURLToPath3(import.meta.url);
29866
29971
  const __dirname2 = dirname5(__filename);
29867
- const skillsDir = join19(__dirname2, "..", "skills");
29972
+ const skillsDir = join20(__dirname2, "..", "skills");
29868
29973
  function parseSkillFrontmatter(content) {
29869
29974
  const match = content.match(/^---\n([\s\S]*?)\n---/);
29870
29975
  if (!match) return null;
@@ -29882,7 +29987,7 @@ function createServer(adapter2, config2) {
29882
29987
  const mdFiles = files.filter((f) => f.endsWith(".md"));
29883
29988
  const prompts = [];
29884
29989
  for (const file of mdFiles) {
29885
- const content = await readFile9(join19(skillsDir, file), "utf-8");
29990
+ const content = await readFile9(join20(skillsDir, file), "utf-8");
29886
29991
  const meta = parseSkillFrontmatter(content);
29887
29992
  if (meta) {
29888
29993
  prompts.push({ name: meta.name, description: meta.description });
@@ -29898,7 +30003,7 @@ function createServer(adapter2, config2) {
29898
30003
  try {
29899
30004
  const files = await readdir4(skillsDir);
29900
30005
  for (const file of files.filter((f) => f.endsWith(".md"))) {
29901
- const content = await readFile9(join19(skillsDir, file), "utf-8");
30006
+ const content = await readFile9(join20(skillsDir, file), "utf-8");
29902
30007
  const meta = parseSkillFrontmatter(content);
29903
30008
  if (meta?.name === name) {
29904
30009
  const body = content.replace(/^---\n[\s\S]*?\n---\n*/, "");
@@ -30598,7 +30703,7 @@ async function dispatchRequest(args) {
30598
30703
  var __dirname = dirname6(fileURLToPath4(import.meta.url));
30599
30704
  var pkgVersion = "unknown";
30600
30705
  try {
30601
- const pkg = JSON.parse(readFileSync17(join24(__dirname, "..", "package.json"), "utf-8"));
30706
+ const pkg = JSON.parse(readFileSync18(join25(__dirname, "..", "package.json"), "utf-8"));
30602
30707
  pkgVersion = pkg.version;
30603
30708
  } catch {
30604
30709
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papi-ai/server",
3
- "version": "0.7.66",
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",