@papi-ai/server 0.7.84 → 0.7.98

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/prompts.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/lib/carry-forward-shape.ts
2
+ import { splitCarryForward, renderCarryForward } from "@papi-ai/shared";
3
+
1
4
  // src/prompts.ts
2
5
  var AD_REJECTION_RULES = `**AD Minting Guard \u2014 REJECT observations dressed as decisions.**
3
6
 
@@ -30,7 +33,11 @@ The guard above says what to reject. This says what to propose. Rejecting is not
30
33
 
31
34
  **Copy variants are NEVER decisions.** A tagline, a headline, a value proposition, a piece of marketing or product wording: swapping one for another constrains no future work, so it fails test (b) outright. Wording changes as often as the market teaches you something, and routing every edit through decision ceremony is what buries the handful of decisions that genuinely constrain the project. Edit the wording where the wording lives. If two variants are being compared against a measurement, that is an experiment, not a decision.
32
35
 
33
- **A decision decaying into a convention is the healthy path, not a failure.** When a live AD stops being arguable \u2014 the alternatives are no longer on the table and nobody would re-litigate it \u2014 say so during housekeeping and propose retiring it into a convention. An AD registry where most entries are settled is one nobody reads.`;
36
+ **A decision decaying into a convention is the healthy path, not a failure.** When a live AD stops being arguable \u2014 the alternatives are no longer on the table and nobody would re-litigate it \u2014 propose demoting it during housekeeping with \`action: "demote"\`. An AD registry where most entries are settled is one nobody reads.
37
+
38
+ DEMOTION IS THE THIRD TRANSITION, and it is not either of the other two. \`supersede\` replaces a stance with a NEW one; \`delete\` removes something that was never a decision. \`demote\` is for a decision that was RIGHT and still holds \u2014 it keeps riding every build handoff as a convention and simply stops occupying decision surface. Apply test (c) to each live AD: could a competent person argue the other side TODAY, on current evidence? If no, propose \`demote\` and give the one-line reason it is no longer contested.
39
+
40
+ PROPOSE, NEVER ASSUME. A demotion is a judgement about whether something is still arguable, and the owner is the one who knows. Surface candidates with reasoning and let the human confirm on apply, exactly as board corrections and cancellations are gated. Do not demote in bulk to tidy the registry.`;
34
41
  var AD_CONFLICT_SURFACING_RULES = `**A contradiction is NOT a veto \u2014 surface it, never silently shelve it.**
35
42
 
36
43
  Active Decisions are *active*: they can be superseded, modified, or abandoned. You do NOT have authority to kill a piece of work simply because it cuts against one. That is the user's call, and they can only make it if you show it to them.
@@ -92,7 +99,7 @@ SECURITY CONSIDERATIONS
92
99
  [data exposure, secrets handling, auth/access control, dependency risks \u2014 or "None \u2014 no security-relevant changes"]
93
100
 
94
101
  DEPLOY VERIFICATION
95
- [Include this section ONLY when FILES LIKELY TOUCHED contains a TRIGGER-SURFACE path. Trigger surfaces (exact set): lib/install-snippets.ts, packages/server/src/transport-http.ts, packages/server/src/index.ts, app/well-known/**, app/api/auth/oauth/**, app/proxy.ts, app/middleware.ts, lib/auth*, next.config.ts, vercel.json, **/railway.toml, **/Dockerfile, **/Procfile, supabase/functions/**, lib/env*.ts. When required, instruct the builder: "Production verification REQUIRED. After deploy, run a curl against the live URL(s) you changed and pass production_verification on build_execute complete with { urls, curl_command, http_status, response_excerpt, verified_at }. http_status MUST be 2xx; build_execute and review_submit reject otherwise. Server inspects the diff against origin/main to enforce this." Omit this section entirely when no trigger surface is touched.]
102
+ [Include this section ONLY when FILES LIKELY TOUCHED contains a TRIGGER-SURFACE path. Trigger surfaces (exact set): lib/install-snippets.ts, packages/server/src/transport-http.ts, packages/server/src/index.ts, app/well-known/**, app/api/auth/oauth/**, app/proxy.ts, app/middleware.ts, lib/auth*, next.config.ts, **/Dockerfile, **/Procfile, supabase/functions/**, lib/env*.ts. When required, instruct the builder: "Production verification REQUIRED. After deploy, run a curl against the live URL(s) you changed and pass production_verification on build_execute complete with { urls, curl_command, http_status, response_excerpt, verified_at }. http_status MUST be 2xx; build_execute and review_submit reject otherwise. Server inspects the diff against origin/main to enforce this." Omit this section entirely when no trigger surface is touched.]
96
103
 
97
104
  REFERENCE DOCS
98
105
  [Optional \u2014 paths to docs/ files that provide background context for this task. Include only when the task originated from research or scoping work and the doc contains context the builder will need beyond what is in this handoff. Omit this section entirely for tasks that don't need supplementary context.]
@@ -300,7 +307,7 @@ var PLAN_FRAGMENT_MARKETING_BRIEF = `
300
307
  Add to ACCEPTANCE CRITERIA: "[ ] Message Frame confirmed with Owner before drafting" and "[ ] Final content reviewed by Owner before publishing."`;
301
308
  var PLAN_FRAGMENT_OPS_BRIEF = `
302
309
  **Ops brief task detection:** When a task's task type is "ops-brief", generate an OPS BRIEF handoff. Inside SCOPE (DO THIS), use these subsections. KEEP the "SCOPE (DO THIS)" header and put these subsections INSIDE it \u2014 the handoff parser only recognises the standard headers (packages/adapter-md build-handoff.ts SECTION_HEADERS) and a handoff with no SCOPE section is rejected outright (task-3050, C357):
303
- - SYSTEM: Which system or service this ops task touches \u2014 Vercel, Railway, Supabase, GitHub Actions, DNS, etc.
310
+ - SYSTEM: Which system or service this ops task touches \u2014 Vercel, Supabase, GitHub Actions, DNS, etc.
304
311
  - RISK: What could go wrong \u2014 data loss, downtime, broken deployments. Include estimated blast radius (e.g. "affects all authenticated users").
305
312
  - ROLLBACK PLAN: Exact steps to undo the change if something breaks. Must be specific enough to execute under pressure.
306
313
  - DONE CONDITION: The specific observable state that confirms the task is complete \u2014 a health check URL, a metric, a log line, a manual verification step.
@@ -328,7 +335,19 @@ var PLAN_FRAGMENT_FORWARD_HORIZON = `
328
335
  - **Actionable** \u2014 frame as a decision to make, not a vague warning (e.g. "Decide whether to use WebSockets or SSE for real-time updates before starting Phase 4: Real-Time Features")
329
336
  - **Tied to trajectory** \u2014 based on current board state, ADs, and velocity, not generic advice
330
337
  If the Forward Horizon context is absent or there are no meaningful decisions to surface, omit this section entirely. Do NOT generate generic advice like "plan ahead" or "consider testing".`;
331
- function composeFullModeInstructions(flags, ctx) {
338
+ function omitSection(source, startMarker, endMarker) {
339
+ const start = source.indexOf(startMarker);
340
+ if (start === -1) return source;
341
+ const end = source.indexOf(endMarker, start + startMarker.length);
342
+ if (end === -1) return source;
343
+ return source.slice(0, start) + source.slice(end);
344
+ }
345
+ var ALL_PLAN_GATES_ON = {
346
+ planHealthCheck: true,
347
+ backlogTriage: true,
348
+ buildOrder: true
349
+ };
350
+ function composeFullModeInstructions(flags, ctx, gates = ALL_PLAN_GATES_ON) {
332
351
  const parts = [
333
352
  `## FULL MODE
334
353
 
@@ -461,7 +480,21 @@ ${AD_ADMISSION_RULES}
461
480
  }
462
481
  parts.push(`
463
482
  **CRITICAL: Review your Part 2 JSON before finishing. Every action from Part 1 must have a corresponding entry in Part 2. If Part 1 mentions corrections, new tasks, AD changes, or handoffs but Part 2 has empty arrays \u2014 you have a persistence bug.**`);
464
- return parts.join("\n");
483
+ let composed = parts.join("\n");
484
+ if (!gates.planHealthCheck) {
485
+ composed = omitSection(composed, "1. **Cycle Health Check**", "\n2. **Inbox Triage**");
486
+ }
487
+ if (!gates.backlogTriage) {
488
+ composed = omitSection(composed, "2. **Inbox Triage**", "\n3. **Board Integrity**");
489
+ }
490
+ if (!gates.buildOrder) {
491
+ composed = omitSection(
492
+ composed,
493
+ " **Intra-cycle dependency detection:**",
494
+ " **Branch-coherence check (task-1908):**"
495
+ );
496
+ }
497
+ return composed;
465
498
  }
466
499
  var PLAN_FULL_BASELINE_FLAGS = {
467
500
  hasBugTasks: true,
@@ -479,9 +512,11 @@ var PLAN_FULL_INSTRUCTIONS = composeFullModeInstructions(PLAN_FULL_BASELINE_FLAG
479
512
  hasDiscoveryCanvas: true,
480
513
  hasHorizonContext: true
481
514
  });
482
- function buildPlanFullInstructionsConditional(flags, ctx) {
483
- if (!flags || !ctx) return PLAN_FULL_INSTRUCTIONS;
484
- return composeFullModeInstructions(flags, ctx);
515
+ function buildPlanFullInstructionsConditional(flags, ctx, gates) {
516
+ if (!flags || !ctx) {
517
+ return gates ? composeFullModeInstructions(PLAN_FULL_BASELINE_FLAGS, { hasDiscoveryCanvas: true, hasHorizonContext: true }, gates) : PLAN_FULL_INSTRUCTIONS;
518
+ }
519
+ return composeFullModeInstructions(flags, ctx, gates);
485
520
  }
486
521
  var CYCLE_DENSITY_TARGETS = {
487
522
  light: { label: "Light", range: "2-3" },
@@ -522,10 +557,14 @@ function buildPlanUserMessage(ctx) {
522
557
  if (ctx.mode === "bootstrap") {
523
558
  parts.push(PLAN_BOOTSTRAP_INSTRUCTIONS);
524
559
  } else {
525
- const instructions = ctx.boardFlags ? buildPlanFullInstructionsConditional(ctx.boardFlags, {
526
- hasDiscoveryCanvas: !!ctx.discoveryCanvas,
527
- hasHorizonContext: !!ctx.horizonContext
528
- }) : PLAN_FULL_INSTRUCTIONS;
560
+ const instructions = ctx.boardFlags ? buildPlanFullInstructionsConditional(
561
+ ctx.boardFlags,
562
+ {
563
+ hasDiscoveryCanvas: !!ctx.discoveryCanvas,
564
+ hasHorizonContext: !!ctx.horizonContext
565
+ },
566
+ ctx.planGates
567
+ ) : buildPlanFullInstructionsConditional(void 0, void 0, ctx.planGates);
529
568
  parts.push(instructions);
530
569
  }
531
570
  if (ctx.foundationalTasksGuidance) {
@@ -798,11 +837,31 @@ function coerceToString(value) {
798
837
  if (value === null || value === void 0) return "";
799
838
  return JSON.stringify(value, null, 2);
800
839
  }
840
+ function normalizePlannerPriority(value) {
841
+ const raw = coerceToString(value).trim();
842
+ const aliases = {
843
+ p0: "P0 Critical",
844
+ "p0 critical": "P0 Critical",
845
+ p1: "P1 High",
846
+ "p1 high": "P1 High",
847
+ p2: "P2 Medium",
848
+ "p2 medium": "P2 Medium",
849
+ p3: "P3 Low",
850
+ "p3 low": "P3 Low"
851
+ };
852
+ return aliases[raw.toLowerCase()] ?? raw;
853
+ }
801
854
  function coerceCarryForward(value) {
802
855
  if (value === null || value === void 0) return { value: null };
803
856
  if (typeof value === "string") {
804
857
  const trimmed = value.trim();
805
- return { value: trimmed.length > 0 ? trimmed : null };
858
+ if (trimmed.length === 0) return { value: null };
859
+ if (!splitCarryForward(trimmed).split) {
860
+ const warning2 = 'cycleLogCarryForward was persisted, but it has neither a "WHAT SHIPS FOR USERS:" nor a "RELEASE MECHANICS:" label \u2014 orient will render it verbatim instead of leading with product signal. Re-run plan apply with both labels in cycleLogCarryForward for this cycle if that split matters here.';
861
+ console.error(`[plan] ${warning2}`);
862
+ return { value: trimmed, warning: warning2 };
863
+ }
864
+ return { value: trimmed };
806
865
  }
807
866
  const shape = Array.isArray(value) ? "array" : typeof value;
808
867
  const warning = `cycleLogCarryForward was ${shape}, not a string \u2014 DROPPED rather than persisted. Carry-forward is prose that orient parses for the WHAT SHIPS FOR USERS / RELEASE MECHANICS labels; a non-string value cannot carry them. Re-run plan apply with cycleLogCarryForward as a single string (or null) to record one for this cycle.`;
@@ -826,7 +885,7 @@ function coerceStructuredOutput(parsed) {
826
885
  tempId: t.tempId !== void 0 && t.tempId !== null ? coerceToString(t.tempId) : void 0,
827
886
  title: coerceToString(t.title),
828
887
  status: coerceToString(t.status),
829
- priority: coerceToString(t.priority),
888
+ priority: normalizePlannerPriority(t.priority),
830
889
  complexity: coerceToString(t.complexity),
831
890
  module: coerceToString(t.module),
832
891
  epic: coerceToString(t.epic),
@@ -839,7 +898,7 @@ function coerceStructuredOutput(parsed) {
839
898
  const updates = typeof c.updates === "object" && c.updates !== null ? c.updates : {};
840
899
  const coercedUpdates = {};
841
900
  for (const [key, val] of Object.entries(updates)) {
842
- coercedUpdates[key] = typeof val === "object" && val !== null ? JSON.stringify(val) : val;
901
+ coercedUpdates[key] = key === "priority" ? normalizePlannerPriority(val) : typeof val === "object" && val !== null ? JSON.stringify(val) : val;
843
902
  }
844
903
  return { taskId: coerceToString(c.taskId), updates: coercedUpdates };
845
904
  }) : [];
@@ -1017,7 +1076,7 @@ After your natural language output, include this EXACT format on its own line:
1017
1076
  "activeDecisionUpdates": [
1018
1077
  {
1019
1078
  "id": "string \u2014 AD-N (existing) or new AD-N (for new decisions)",
1020
- "action": "confidence_change | modify | resolve | supersede | new | delete",
1079
+ "action": "confidence_change | modify | resolve | supersede | new | delete | demote",
1021
1080
  "body": "string \u2014 full AD block including ### heading, confidence tag, and body text (empty string for delete)",
1022
1081
  "evidenceRef": "string (optional) \u2014 for DELIBERATE decisions (modify / resolve / delete = validate/modify/invalidate), a pointer to the evidence that justified the change: a doc path (docs/research/foo.md), a build-report id, or a metric name. Builds the decision->outcome ledger. Omit if no concrete evidence.",
1023
1082
  "metricDelta": "object (optional) \u2014 { "metric": string, "before": number, "after": number, "delta": number } \u2014 which metric moved and by how much. Use a REAL metric name from cycle_metrics_snapshots where possible (e.g. scope_accuracy, velocity, est_actual_drift). Omit if no metric moved."
@@ -1269,7 +1328,7 @@ After your natural language output, include this EXACT format on its own line:
1269
1328
  "activeDecisionUpdates": [
1270
1329
  {
1271
1330
  "id": "string \u2014 AD-N (existing) or new AD-N (for new decisions)",
1272
- "action": "confidence_change | modify | resolve | supersede | new | delete",
1331
+ "action": "confidence_change | modify | resolve | supersede | new | delete | demote",
1273
1332
  "body": "string \u2014 full AD block including ### heading, confidence tag, and body text (empty string for delete)",
1274
1333
  "evidenceRef": "string (optional) \u2014 for DELIBERATE changes (modify / resolve / delete), a pointer to the justifying evidence: a doc path, a build-report id, or a metric name. Builds the decision->outcome ledger. Omit if none.",
1275
1334
  "metricDelta": "object (optional) \u2014 { "metric": string, "before": number, "after": number, "delta": number } \u2014 which metric moved. Prefer a real metric name from cycle_metrics_snapshots. Omit if none."
@@ -1761,6 +1820,7 @@ export {
1761
1820
  buildVisionTasksPrompt,
1762
1821
  coerceCarryForward,
1763
1822
  extractDependencyChain,
1823
+ normalizePlannerPriority,
1764
1824
  parseReviewStructuredOutput,
1765
1825
  parseStrategyChangeOutput,
1766
1826
  parseStructuredOutput
@@ -0,0 +1,168 @@
1
+ // src/cli/statusline.ts
2
+ import { createHash } from "crypto";
3
+ import { existsSync, readFileSync, writeFileSync } from "fs";
4
+ import { homedir, tmpdir } from "os";
5
+ import { join } from "path";
6
+ import { pathToFileURL } from "url";
7
+ var CACHE_TTL_MS = 3e4;
8
+ var FETCH_TIMEOUT_MS = 3e3;
9
+ function renderPeekLine(c) {
10
+ const parts = [`PAPI c${c.cycle ?? "?"}`];
11
+ parts.push(`build ${c.inFlight}`);
12
+ parts.push(`review ${c.inReview}`);
13
+ if (c.ownerActions !== null) parts.push(`you ${c.ownerActions}`);
14
+ return parts.join(" | ");
15
+ }
16
+ function cacheFilePath(endpoint, projectId) {
17
+ const hash = createHash("sha256").update(`${endpoint}|${projectId ?? ""}`).digest("hex").slice(0, 16);
18
+ return join(tmpdir(), `papi-statusline-${hash}.json`);
19
+ }
20
+ function readClientCache(path, now = Date.now()) {
21
+ try {
22
+ const hit = JSON.parse(readFileSync(path, "utf-8"));
23
+ if (!hit || typeof hit.expires !== "number" || hit.expires <= now) return void 0;
24
+ return hit.payload;
25
+ } catch {
26
+ return void 0;
27
+ }
28
+ }
29
+ function writeClientCache(path, payload, now = Date.now()) {
30
+ try {
31
+ writeFileSync(path, JSON.stringify({ expires: now + CACHE_TTL_MS, payload }));
32
+ } catch {
33
+ }
34
+ }
35
+ function configFromMcpJson(body) {
36
+ if (!body || typeof body !== "object") return void 0;
37
+ const servers = body.mcpServers;
38
+ if (!servers || typeof servers !== "object") return void 0;
39
+ for (const entry of Object.values(servers)) {
40
+ if (!entry || typeof entry !== "object") continue;
41
+ const s = entry;
42
+ if (typeof s.url !== "string" || !/papi/i.test(s.url)) continue;
43
+ const auth = s.headers?.["Authorization"];
44
+ const authString = typeof auth === "string" ? auth : "";
45
+ const bearer = authString.startsWith("Bearer ") ? authString.slice(7).trim() : "";
46
+ const projectHeader = s.headers?.["x-papi-project-id"];
47
+ if (!bearer) continue;
48
+ return {
49
+ endpoint: new URL(s.url).origin,
50
+ bearer,
51
+ projectId: typeof projectHeader === "string" && projectHeader.length > 0 ? projectHeader : void 0
52
+ };
53
+ }
54
+ return void 0;
55
+ }
56
+ function mergeStatusLine(body, command) {
57
+ const obj = body && typeof body === "object" ? body : {};
58
+ if (obj["statusLine"] !== void 0) return null;
59
+ return { ...obj, statusLine: { type: "command", command } };
60
+ }
61
+ function resolveConfig(argv) {
62
+ const flag = (name) => {
63
+ const i = argv.indexOf(name);
64
+ return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
65
+ };
66
+ const endpoint = flag("--endpoint") ?? process.env["PAPI_ENDPOINT"];
67
+ const bearer = flag("--bearer") ?? process.env["PAPI_BEARER"];
68
+ const projectId = flag("--project") ?? process.env["PAPI_PROJECT_ID"];
69
+ if (endpoint && bearer) return { endpoint: new URL(endpoint).origin, bearer, projectId };
70
+ const mcpJsonPath = join(process.cwd(), ".mcp.json");
71
+ if (existsSync(mcpJsonPath)) {
72
+ try {
73
+ return configFromMcpJson(JSON.parse(readFileSync(mcpJsonPath, "utf-8")));
74
+ } catch {
75
+ return void 0;
76
+ }
77
+ }
78
+ return void 0;
79
+ }
80
+ async function fetchPeek(config) {
81
+ const response = await fetch(`${config.endpoint}/peek`, {
82
+ headers: {
83
+ Authorization: `Bearer ${config.bearer}`,
84
+ ...config.projectId ? { "x-papi-project-id": config.projectId } : {}
85
+ },
86
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
87
+ });
88
+ if (!response.ok) throw new Error(`peek returned ${response.status}`);
89
+ const body = await response.json();
90
+ return {
91
+ cycle: typeof body.cycle === "number" ? body.cycle : null,
92
+ inFlight: typeof body.inFlight === "number" ? body.inFlight : 0,
93
+ inReview: typeof body.inReview === "number" ? body.inReview : 0,
94
+ ownerActions: typeof body.ownerActions === "number" ? body.ownerActions : null
95
+ };
96
+ }
97
+ var RENDER_COMMAND = "npx -y @papi-ai/cli papi-statusline";
98
+ async function install() {
99
+ const settingsPath = join(homedir(), ".claude", "settings.json");
100
+ let body = {};
101
+ if (existsSync(settingsPath)) {
102
+ try {
103
+ body = JSON.parse(readFileSync(settingsPath, "utf-8"));
104
+ } catch (err) {
105
+ console.error(`papi-statusline: could not parse ${settingsPath}: ${err.message}`);
106
+ return 1;
107
+ }
108
+ }
109
+ const merged = mergeStatusLine(body, RENDER_COMMAND);
110
+ if (merged === null) {
111
+ console.log(
112
+ `papi-statusline: ${settingsPath} already has a statusLine entry \u2014 left untouched.
113
+ To switch it to PAPI manually, set:
114
+ "statusLine": { "type": "command", "command": "${RENDER_COMMAND}" }
115
+
116
+ Any other MCP client with a custom status-bar command can run the same command.`
117
+ );
118
+ return 0;
119
+ }
120
+ writeFileSync(settingsPath, `${JSON.stringify(merged, null, 2)}
121
+ `);
122
+ console.log(`papi-statusline: installed into ${settingsPath}. Restart your AI client to see it.`);
123
+ return 0;
124
+ }
125
+ async function runStatusline(argv, stdin) {
126
+ if (argv.includes("--install")) return install();
127
+ void stdin;
128
+ const config = resolveConfig(argv);
129
+ if (!config) return 0;
130
+ const cachePath = cacheFilePath(config.endpoint, config.projectId);
131
+ const cached = readClientCache(cachePath);
132
+ if (cached) {
133
+ process.stdout.write(`${renderPeekLine(cached)}
134
+ `);
135
+ return 0;
136
+ }
137
+ try {
138
+ const payload = await fetchPeek(config);
139
+ writeClientCache(cachePath, payload);
140
+ process.stdout.write(`${renderPeekLine(payload)}
141
+ `);
142
+ } catch {
143
+ }
144
+ return 0;
145
+ }
146
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
147
+ const chunks = [];
148
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
149
+ process.stdin.on("end", () => {
150
+ void runStatusline(process.argv.slice(2), Buffer.concat(chunks).toString("utf-8")).then((code) => {
151
+ process.exitCode = code;
152
+ });
153
+ });
154
+ setTimeout(() => {
155
+ void runStatusline(process.argv.slice(2), Buffer.concat(chunks).toString("utf-8")).then((code) => {
156
+ process.exitCode = code;
157
+ });
158
+ }, 250);
159
+ }
160
+ export {
161
+ cacheFilePath,
162
+ configFromMcpJson,
163
+ mergeStatusLine,
164
+ readClientCache,
165
+ renderPeekLine,
166
+ runStatusline,
167
+ writeClientCache
168
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@papi-ai/server",
3
- "version": "0.7.84",
4
- "description": "PAPI MCP server AI-powered sprint planning, build execution, and strategy review for software projects",
3
+ "version": "0.7.98",
4
+ "description": "PAPI MCP server \u2014 AI-powered sprint planning, build execution, and strategy review for software projects",
5
5
  "license": "Elastic-2.0",
6
6
  "mcpName": "io.github.getpapi/papi",
7
7
  "type": "module",
@@ -49,17 +49,16 @@
49
49
  "node": ">=18.0.0"
50
50
  },
51
51
  "dependencies": {
52
- "@anthropic-ai/sdk": "^0.82.0",
52
+ "@anthropic-ai/sdk": "^0.91.1",
53
53
  "@modelcontextprotocol/sdk": "^1.27.1",
54
- "@papi-ai/adapter-pg": "^0.2.15",
55
- "@papi-ai/shared": "^0.2.4",
56
- "@papi-ai/skills": "^0.1.0",
57
- "js-yaml": "^4.1.0"
54
+ "@papi-ai/adapter-pg": "^0.2.16",
55
+ "@papi-ai/shared": "^0.2.5",
56
+ "@papi-ai/skills": "^0.1.4"
58
57
  },
59
58
  "devDependencies": {
60
59
  "@papi-ai/adapter-md": "^0.2.0",
61
- "@types/js-yaml": "^4.0.9",
62
60
  "@types/node": "^22.0.0",
61
+ "ajv": "^8.20.0",
63
62
  "tsup": "^8.0.0",
64
63
  "typescript": "^5.5.0",
65
64
  "vitest": "^4.0.18"