@papi-ai/server 0.7.41 → 0.7.42
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.
|
@@ -82,7 +82,12 @@ Your output is **not done** if ANY of these is true (these are falsifiable — c
|
|
|
82
82
|
- [ ] Gradient text or gradient fill used decoratively (the generic-AI look).
|
|
83
83
|
- [ ] Your brand accent used as a glow / dark-mode-only sheen instead of solid, intentional colour.
|
|
84
84
|
- [ ] An "earned" signal colour (insight/intelligence/premium) used as a plain decorative fill.
|
|
85
|
-
- [ ]
|
|
85
|
+
- [ ] Two elements competing for dominance (both fight for the eye). NOTE: a page with NO hero is
|
|
86
|
+
fine when nothing must lead — do not manufacture one to satisfy this line; only *competing*
|
|
87
|
+
dominance fails (see The core principle).
|
|
88
|
+
- [ ] Guidance, state, or a next action explained in a sentence of helper text where a visual cue
|
|
89
|
+
(position, size, colour, icon, a signpost, motion-on-change) would carry it — prose used as a
|
|
90
|
+
crutch for missing visual steering.
|
|
86
91
|
- [ ] More than ~3 sections fully visible above the fold.
|
|
87
92
|
- [ ] Identical-sized cards in a uniform row (flat hierarchy — needs a dominant 2×+ cell).
|
|
88
93
|
- [ ] Monospace on labels, status, timestamps, or nav.
|
|
@@ -121,6 +126,16 @@ Return a tight report (this is data, not prose):
|
|
|
121
126
|
|
|
122
127
|
## The core principle
|
|
123
128
|
|
|
124
|
-
**Composition > decoration.** A page with perfect tokens and flat hierarchy is
|
|
125
|
-
slightly-off tokens and a clear
|
|
126
|
-
|
|
129
|
+
**Composition > decoration, and show > tell.** A page with perfect tokens and flat hierarchy is
|
|
130
|
+
worse than one with slightly-off tokens and a clear reading order where every element earns its
|
|
131
|
+
space. Two rules generalist agents keep getting wrong:
|
|
132
|
+
|
|
133
|
+
- **Steer with visuals, not text.** When the user needs guidance — what to do, where to look, what
|
|
134
|
+
state something is in — reach for a VISUAL cue first: position, size, colour, an icon, a
|
|
135
|
+
connecting line, a spatial signpost, motion on change. A sentence of helper text is the last
|
|
136
|
+
resort, not the first. Over-explaining in prose is the default AI failure mode; a well-placed
|
|
137
|
+
arrow, a pulse, or a bolded number beats a paragraph. Visual steering and motion beat words.
|
|
138
|
+
- **A hero is conditional, not mandatory.** Give the page one dominant focal point WHEN it has a
|
|
139
|
+
single thing that must lead. Dense tool, list, board, and settings surfaces can carry a calm, even
|
|
140
|
+
hierarchy — do not manufacture a hero, an oversized stat, or a promoted banner where the content
|
|
141
|
+
has no natural lead. Only *competing* dominance is always wrong; the absence of a hero is not.
|
|
@@ -48,15 +48,17 @@ Plus a one-line page read: `kind / audience / the one thing they must see first`
|
|
|
48
48
|
3. **Read the target page in full** — not just the component you're editing. You need full page
|
|
49
49
|
context to judge compositional hierarchy.
|
|
50
50
|
4. **List all sibling components** on the page. You'll weigh each one's visual weight against the others.
|
|
51
|
-
5. **Check both states:** populated and empty/zero-data. Both need a hero
|
|
51
|
+
5. **Check both states:** populated and empty/zero-data. Both need clear hierarchy (a hero only where one is warranted — see Check 1).
|
|
52
52
|
|
|
53
53
|
## Step 2: The Seven Checks
|
|
54
54
|
|
|
55
55
|
For each, give ✅ Pass / ⚠️ Weak / ❌ Fail plus a one-line reason.
|
|
56
56
|
|
|
57
|
-
### Check 1: Hero Element
|
|
58
|
-
>
|
|
59
|
-
|
|
57
|
+
### Check 1: Hero Element (conditional — NOT every page needs one)
|
|
58
|
+
> Give the page one dominant element WHEN it has one thing that must lead. A hero is not mandatory.
|
|
59
|
+
Does this page have a single thing that must lead? If yes: name it; missing/weak = ⚠️/❌. If no (a
|
|
60
|
+
dense tool / list / board / settings surface): a calm, even hierarchy is ✅ — do NOT manufacture a
|
|
61
|
+
hero to pass this check. Two elements *competing* for dominance is ALWAYS ❌ (the real failure).
|
|
60
62
|
|
|
61
63
|
### Check 2: Reading Order
|
|
62
64
|
> hero → primary action → supporting content → tertiary.
|
|
@@ -101,7 +103,11 @@ Run through every named anti-pattern and flag matches. Common universal ones:
|
|
|
101
103
|
- Gradient text or gradient fill used decoratively.
|
|
102
104
|
- Brand accent as a glow / dark-only sheen instead of solid intentional colour.
|
|
103
105
|
- A "signal" colour used as a plain decorative fill.
|
|
104
|
-
-
|
|
106
|
+
- Two elements competing for dominance (both fight for the eye). NOT a fail: a page with no hero
|
|
107
|
+
when nothing must lead (see Check 1) — only *competing* dominance fails here.
|
|
108
|
+
- Guidance, state, or a next action carried by a sentence of helper text where a visual cue
|
|
109
|
+
(position, size, colour, an icon, a signpost, motion-on-change) would do — prose as a crutch for
|
|
110
|
+
missing visual steering. Prefer showing over telling.
|
|
105
111
|
- Identical-sized cards in a uniform row (flat hierarchy).
|
|
106
112
|
- Monospace on labels / status / timestamps / nav.
|
|
107
113
|
- Header, label, and value at the same size+weight.
|
|
@@ -158,5 +164,10 @@ The pre-build critique catches composition; this catches what's only visible in
|
|
|
158
164
|
|
|
159
165
|
## The Core Principle
|
|
160
166
|
|
|
161
|
-
**Composition > Decoration.** A page with perfect tokens and flat hierarchy is
|
|
162
|
-
slightly-off tokens and clear hierarchy.
|
|
167
|
+
**Composition > Decoration, and show > tell.** A page with perfect tokens and flat hierarchy is
|
|
168
|
+
worse than one with slightly-off tokens and clear hierarchy. Two things generalist agents keep
|
|
169
|
+
getting wrong: (1) **steer with visuals, not text** — reach for position, size, colour, an icon, a
|
|
170
|
+
signpost, or motion-on-change before a sentence of helper text; over-explaining in prose is the
|
|
171
|
+
default AI failure mode. (2) **A hero is conditional** — give a page one dominant element only when
|
|
172
|
+
it has one thing that must lead; dense list / tool / settings surfaces carry a calm, even hierarchy,
|
|
173
|
+
so do not manufacture a hero. Clear reading order, everything earns its space, show before you tell.
|
|
@@ -1012,6 +1012,48 @@ var init_git = __esm({
|
|
|
1012
1012
|
}
|
|
1013
1013
|
});
|
|
1014
1014
|
|
|
1015
|
+
// src/lib/install-id.ts
|
|
1016
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
1017
|
+
import { mkdirSync, readFileSync, writeFileSync, chmodSync } from "fs";
|
|
1018
|
+
import { homedir } from "os";
|
|
1019
|
+
import { join as join2 } from "path";
|
|
1020
|
+
var PAPI_HOME_DIR, INSTALL_ID_FILE;
|
|
1021
|
+
var init_install_id = __esm({
|
|
1022
|
+
"src/lib/install-id.ts"() {
|
|
1023
|
+
"use strict";
|
|
1024
|
+
PAPI_HOME_DIR = join2(homedir(), ".papi");
|
|
1025
|
+
INSTALL_ID_FILE = join2(PAPI_HOME_DIR, "install-id.json");
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
// src/lib/telemetry.ts
|
|
1030
|
+
function reportTelemetryEmitFailure(reason, ctx) {
|
|
1031
|
+
consecutiveTelemetryFailures += 1;
|
|
1032
|
+
console.error(
|
|
1033
|
+
`[telemetry] emit failed (${reason}) tool=${ctx.toolName} event=${ctx.eventType} project=${ctx.projectId}`
|
|
1034
|
+
);
|
|
1035
|
+
if (consecutiveTelemetryFailures >= TELEMETRY_BLACKOUT_THRESHOLD) {
|
|
1036
|
+
console.error(
|
|
1037
|
+
`[telemetry] BLACKOUT: ${consecutiveTelemetryFailures} consecutive telemetry emit failures \u2014 activation metrics are under-counting for this pod. Check the data-proxy /telemetry path (auth/RLS/network/schema).`
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
function noteTelemetryEmitSuccess() {
|
|
1042
|
+
consecutiveTelemetryFailures = 0;
|
|
1043
|
+
}
|
|
1044
|
+
var HOSTED_SUPABASE_URL, DEFAULT_TELEMETRY_ENDPOINT, MD_PINGS_SUPABASE_URL, consecutiveTelemetryFailures, TELEMETRY_BLACKOUT_THRESHOLD;
|
|
1045
|
+
var init_telemetry = __esm({
|
|
1046
|
+
"src/lib/telemetry.ts"() {
|
|
1047
|
+
"use strict";
|
|
1048
|
+
init_install_id();
|
|
1049
|
+
HOSTED_SUPABASE_URL = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
|
|
1050
|
+
DEFAULT_TELEMETRY_ENDPOINT = `${HOSTED_SUPABASE_URL}/functions/v1/data-proxy`;
|
|
1051
|
+
MD_PINGS_SUPABASE_URL = process.env["PAPI_MD_PINGS_URL"] ?? HOSTED_SUPABASE_URL;
|
|
1052
|
+
consecutiveTelemetryFailures = 0;
|
|
1053
|
+
TELEMETRY_BLACKOUT_THRESHOLD = 3;
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1015
1057
|
// src/proxy-adapter.ts
|
|
1016
1058
|
var proxy_adapter_exports = {};
|
|
1017
1059
|
__export(proxy_adapter_exports, {
|
|
@@ -1058,6 +1100,7 @@ var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, ProxyPapiAdapter;
|
|
|
1058
1100
|
var init_proxy_adapter = __esm({
|
|
1059
1101
|
"src/proxy-adapter.ts"() {
|
|
1060
1102
|
"use strict";
|
|
1103
|
+
init_telemetry();
|
|
1061
1104
|
JSONB_PASSTHROUGH_KEYS = /* @__PURE__ */ new Set([
|
|
1062
1105
|
"buildHandoff",
|
|
1063
1106
|
"stateHistory",
|
|
@@ -1420,6 +1463,7 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1420
1463
|
* telemetry failure must never surface as a tool error.
|
|
1421
1464
|
*/
|
|
1422
1465
|
emitTelemetry(event) {
|
|
1466
|
+
const ctx = { projectId: event.projectId, toolName: event.toolName, eventType: event.eventType };
|
|
1423
1467
|
fetch(`${this.endpoint}/telemetry`, {
|
|
1424
1468
|
method: "POST",
|
|
1425
1469
|
headers: {
|
|
@@ -1433,7 +1477,11 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1433
1477
|
metadata: event.metadata ?? {}
|
|
1434
1478
|
}),
|
|
1435
1479
|
signal: AbortSignal.timeout(5e3)
|
|
1436
|
-
}).
|
|
1480
|
+
}).then((res) => {
|
|
1481
|
+
if (res.ok) noteTelemetryEmitSuccess();
|
|
1482
|
+
else reportTelemetryEmitFailure(`HTTP ${res.status}`, ctx);
|
|
1483
|
+
}).catch((err) => {
|
|
1484
|
+
reportTelemetryEmitFailure(err instanceof Error ? err.name : "network error", ctx);
|
|
1437
1485
|
});
|
|
1438
1486
|
}
|
|
1439
1487
|
readToolMetrics() {
|
|
@@ -4064,8 +4112,8 @@ function detectUserId() {
|
|
|
4064
4112
|
}
|
|
4065
4113
|
return void 0;
|
|
4066
4114
|
}
|
|
4067
|
-
var
|
|
4068
|
-
var HOSTED_PROXY_ENDPOINT = `${
|
|
4115
|
+
var HOSTED_SUPABASE_URL2 = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
|
|
4116
|
+
var HOSTED_PROXY_ENDPOINT = `${HOSTED_SUPABASE_URL2}/functions/v1/data-proxy`;
|
|
4069
4117
|
var PLACEHOLDER_PATTERNS = [
|
|
4070
4118
|
"<YOUR_DATABASE_URL>",
|
|
4071
4119
|
"your-database-url",
|
package/dist/index.js
CHANGED
|
@@ -1013,6 +1013,164 @@ var init_git = __esm({
|
|
|
1013
1013
|
}
|
|
1014
1014
|
});
|
|
1015
1015
|
|
|
1016
|
+
// src/lib/install-id.ts
|
|
1017
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
1018
|
+
import { mkdirSync, readFileSync, writeFileSync, chmodSync } from "fs";
|
|
1019
|
+
import { homedir } from "os";
|
|
1020
|
+
import { join as join2 } from "path";
|
|
1021
|
+
function isValidUuid(s) {
|
|
1022
|
+
return typeof s === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);
|
|
1023
|
+
}
|
|
1024
|
+
function getInstallId() {
|
|
1025
|
+
if (cachedInstallId) return cachedInstallId;
|
|
1026
|
+
try {
|
|
1027
|
+
const raw = readFileSync(INSTALL_ID_FILE, "utf-8");
|
|
1028
|
+
const parsed = JSON.parse(raw);
|
|
1029
|
+
if (isValidUuid(parsed.install_id)) {
|
|
1030
|
+
cachedInstallId = parsed.install_id;
|
|
1031
|
+
return cachedInstallId;
|
|
1032
|
+
}
|
|
1033
|
+
} catch {
|
|
1034
|
+
}
|
|
1035
|
+
try {
|
|
1036
|
+
mkdirSync(PAPI_HOME_DIR, { recursive: true, mode: 448 });
|
|
1037
|
+
const id = randomUUID7();
|
|
1038
|
+
const contents = {
|
|
1039
|
+
install_id: id,
|
|
1040
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1041
|
+
};
|
|
1042
|
+
writeFileSync(INSTALL_ID_FILE, JSON.stringify(contents, null, 2), { mode: 384 });
|
|
1043
|
+
try {
|
|
1044
|
+
chmodSync(INSTALL_ID_FILE, 384);
|
|
1045
|
+
} catch {
|
|
1046
|
+
}
|
|
1047
|
+
cachedInstallId = id;
|
|
1048
|
+
return cachedInstallId;
|
|
1049
|
+
} catch {
|
|
1050
|
+
return null;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
var PAPI_HOME_DIR, INSTALL_ID_FILE, cachedInstallId;
|
|
1054
|
+
var init_install_id = __esm({
|
|
1055
|
+
"src/lib/install-id.ts"() {
|
|
1056
|
+
"use strict";
|
|
1057
|
+
PAPI_HOME_DIR = join2(homedir(), ".papi");
|
|
1058
|
+
INSTALL_ID_FILE = join2(PAPI_HOME_DIR, "install-id.json");
|
|
1059
|
+
cachedInstallId = null;
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
// src/lib/telemetry.ts
|
|
1064
|
+
function isEnabled() {
|
|
1065
|
+
const val = process.env["PAPI_TELEMETRY"];
|
|
1066
|
+
return val !== "false" && val !== "off";
|
|
1067
|
+
}
|
|
1068
|
+
function reportTelemetryEmitFailure(reason, ctx) {
|
|
1069
|
+
consecutiveTelemetryFailures += 1;
|
|
1070
|
+
console.error(
|
|
1071
|
+
`[telemetry] emit failed (${reason}) tool=${ctx.toolName} event=${ctx.eventType} project=${ctx.projectId}`
|
|
1072
|
+
);
|
|
1073
|
+
if (consecutiveTelemetryFailures >= TELEMETRY_BLACKOUT_THRESHOLD) {
|
|
1074
|
+
console.error(
|
|
1075
|
+
`[telemetry] BLACKOUT: ${consecutiveTelemetryFailures} consecutive telemetry emit failures \u2014 activation metrics are under-counting for this pod. Check the data-proxy /telemetry path (auth/RLS/network/schema).`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function noteTelemetryEmitSuccess() {
|
|
1080
|
+
consecutiveTelemetryFailures = 0;
|
|
1081
|
+
}
|
|
1082
|
+
function emitTelemetryEvent(event) {
|
|
1083
|
+
if (!isEnabled()) return;
|
|
1084
|
+
const apiKey = process.env["PAPI_DATA_API_KEY"];
|
|
1085
|
+
if (!apiKey) return;
|
|
1086
|
+
const endpoint = process.env["PAPI_DATA_ENDPOINT"] ?? DEFAULT_TELEMETRY_ENDPOINT;
|
|
1087
|
+
const body = {
|
|
1088
|
+
projectId: event.project_id,
|
|
1089
|
+
toolName: event.tool_name,
|
|
1090
|
+
eventType: event.event_type,
|
|
1091
|
+
metadata: event.metadata ?? {}
|
|
1092
|
+
};
|
|
1093
|
+
const ctx = {
|
|
1094
|
+
projectId: event.project_id,
|
|
1095
|
+
toolName: event.tool_name,
|
|
1096
|
+
eventType: event.event_type
|
|
1097
|
+
};
|
|
1098
|
+
fetch(`${endpoint}/telemetry`, {
|
|
1099
|
+
method: "POST",
|
|
1100
|
+
headers: {
|
|
1101
|
+
"Content-Type": "application/json",
|
|
1102
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1103
|
+
},
|
|
1104
|
+
body: JSON.stringify(body),
|
|
1105
|
+
signal: AbortSignal.timeout(5e3)
|
|
1106
|
+
}).then((res) => {
|
|
1107
|
+
if (res.ok) noteTelemetryEmitSuccess();
|
|
1108
|
+
else reportTelemetryEmitFailure(`HTTP ${res.status}`, ctx);
|
|
1109
|
+
}).catch((err) => {
|
|
1110
|
+
reportTelemetryEmitFailure(err instanceof Error ? err.name : "network error", ctx);
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
function emitToolCall(projectId, toolName, durationMs, extra) {
|
|
1114
|
+
emitTelemetryEvent({
|
|
1115
|
+
project_id: projectId,
|
|
1116
|
+
tool_name: toolName,
|
|
1117
|
+
event_type: "tool_call",
|
|
1118
|
+
metadata: { duration_ms: durationMs, ...extra }
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
function emitMdAdapterPing(toolName, extra, userId, projectSlug) {
|
|
1122
|
+
if (!isEnabled()) return;
|
|
1123
|
+
const installId = getInstallId();
|
|
1124
|
+
if (!installId) return;
|
|
1125
|
+
const resolvedUserId = userId ?? process.env["PAPI_USER_ID"] ?? void 0;
|
|
1126
|
+
const body = {
|
|
1127
|
+
install_id: installId,
|
|
1128
|
+
tool_name: toolName,
|
|
1129
|
+
papi_version: process.env["npm_package_version"] ?? null,
|
|
1130
|
+
metadata: extra ?? {}
|
|
1131
|
+
};
|
|
1132
|
+
if (resolvedUserId) body["user_id"] = resolvedUserId;
|
|
1133
|
+
if (projectSlug) body["project_slug"] = projectSlug;
|
|
1134
|
+
fetch(`${MD_PINGS_SUPABASE_URL}/rest/v1/md_adapter_pings`, {
|
|
1135
|
+
method: "POST",
|
|
1136
|
+
headers: {
|
|
1137
|
+
"Content-Type": "application/json",
|
|
1138
|
+
"apikey": MD_PINGS_ANON_KEY,
|
|
1139
|
+
"Authorization": `Bearer ${MD_PINGS_ANON_KEY}`,
|
|
1140
|
+
"Prefer": "return=minimal"
|
|
1141
|
+
},
|
|
1142
|
+
body: JSON.stringify(body),
|
|
1143
|
+
signal: AbortSignal.timeout(5e3)
|
|
1144
|
+
}).catch(() => {
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
function emitMilestone(projectId, milestone, extra) {
|
|
1148
|
+
emitTelemetryEvent({
|
|
1149
|
+
project_id: projectId,
|
|
1150
|
+
tool_name: milestone,
|
|
1151
|
+
event_type: "milestone",
|
|
1152
|
+
metadata: extra
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
function resolveTelemetryProjectId(config2) {
|
|
1156
|
+
if (config2.projectId && config2.projectId.length > 0) return config2.projectId;
|
|
1157
|
+
const env = process.env["PAPI_PROJECT_ID"];
|
|
1158
|
+
return env && env.length > 0 ? env : void 0;
|
|
1159
|
+
}
|
|
1160
|
+
var HOSTED_SUPABASE_URL, DEFAULT_TELEMETRY_ENDPOINT, MD_PINGS_SUPABASE_URL, MD_PINGS_ANON_KEY, consecutiveTelemetryFailures, TELEMETRY_BLACKOUT_THRESHOLD;
|
|
1161
|
+
var init_telemetry = __esm({
|
|
1162
|
+
"src/lib/telemetry.ts"() {
|
|
1163
|
+
"use strict";
|
|
1164
|
+
init_install_id();
|
|
1165
|
+
HOSTED_SUPABASE_URL = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
|
|
1166
|
+
DEFAULT_TELEMETRY_ENDPOINT = `${HOSTED_SUPABASE_URL}/functions/v1/data-proxy`;
|
|
1167
|
+
MD_PINGS_SUPABASE_URL = process.env["PAPI_MD_PINGS_URL"] ?? HOSTED_SUPABASE_URL;
|
|
1168
|
+
MD_PINGS_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd1ZXdneWdjcGNtcmNvcHBpaHp4Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzI2Njk2NTMsImV4cCI6MjA4ODI0NTY1M30.V5Jw7wJgiMpSQPa2mt0ftjyye5ynG1qLlam00yPVNJY";
|
|
1169
|
+
consecutiveTelemetryFailures = 0;
|
|
1170
|
+
TELEMETRY_BLACKOUT_THRESHOLD = 3;
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1016
1174
|
// src/proxy-adapter.ts
|
|
1017
1175
|
var proxy_adapter_exports = {};
|
|
1018
1176
|
__export(proxy_adapter_exports, {
|
|
@@ -1059,6 +1217,7 @@ var JSONB_PASSTHROUGH_KEYS, DISPLAY_ID_METHODS, ProxyPapiAdapter;
|
|
|
1059
1217
|
var init_proxy_adapter = __esm({
|
|
1060
1218
|
"src/proxy-adapter.ts"() {
|
|
1061
1219
|
"use strict";
|
|
1220
|
+
init_telemetry();
|
|
1062
1221
|
JSONB_PASSTHROUGH_KEYS = /* @__PURE__ */ new Set([
|
|
1063
1222
|
"buildHandoff",
|
|
1064
1223
|
"stateHistory",
|
|
@@ -1421,6 +1580,7 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1421
1580
|
* telemetry failure must never surface as a tool error.
|
|
1422
1581
|
*/
|
|
1423
1582
|
emitTelemetry(event) {
|
|
1583
|
+
const ctx = { projectId: event.projectId, toolName: event.toolName, eventType: event.eventType };
|
|
1424
1584
|
fetch(`${this.endpoint}/telemetry`, {
|
|
1425
1585
|
method: "POST",
|
|
1426
1586
|
headers: {
|
|
@@ -1434,7 +1594,11 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1434
1594
|
metadata: event.metadata ?? {}
|
|
1435
1595
|
}),
|
|
1436
1596
|
signal: AbortSignal.timeout(5e3)
|
|
1437
|
-
}).
|
|
1597
|
+
}).then((res) => {
|
|
1598
|
+
if (res.ok) noteTelemetryEmitSuccess();
|
|
1599
|
+
else reportTelemetryEmitFailure(`HTTP ${res.status}`, ctx);
|
|
1600
|
+
}).catch((err) => {
|
|
1601
|
+
reportTelemetryEmitFailure(err instanceof Error ? err.name : "network error", ctx);
|
|
1438
1602
|
});
|
|
1439
1603
|
}
|
|
1440
1604
|
readToolMetrics() {
|
|
@@ -7440,8 +7604,8 @@ function detectUserId() {
|
|
|
7440
7604
|
}
|
|
7441
7605
|
return void 0;
|
|
7442
7606
|
}
|
|
7443
|
-
var
|
|
7444
|
-
var HOSTED_PROXY_ENDPOINT = `${
|
|
7607
|
+
var HOSTED_SUPABASE_URL2 = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
|
|
7608
|
+
var HOSTED_PROXY_ENDPOINT = `${HOSTED_SUPABASE_URL2}/functions/v1/data-proxy`;
|
|
7445
7609
|
var PLACEHOLDER_PATTERNS = [
|
|
7446
7610
|
"<YOUR_DATABASE_URL>",
|
|
7447
7611
|
"your-database-url",
|
|
@@ -7821,7 +7985,7 @@ function errorResponse(message) {
|
|
|
7821
7985
|
}
|
|
7822
7986
|
|
|
7823
7987
|
// src/services/plan.ts
|
|
7824
|
-
import { createHash, randomUUID as
|
|
7988
|
+
import { createHash, randomUUID as randomUUID8 } from "crypto";
|
|
7825
7989
|
import { readFile as readFile2 } from "fs/promises";
|
|
7826
7990
|
import path4 from "path";
|
|
7827
7991
|
|
|
@@ -11447,7 +11611,7 @@ ${cleanContent}`;
|
|
|
11447
11611
|
const payload = {
|
|
11448
11612
|
cycleNumber: newCycleNumber,
|
|
11449
11613
|
cycleLog: {
|
|
11450
|
-
uuid:
|
|
11614
|
+
uuid: randomUUID8(),
|
|
11451
11615
|
cycleNumber: newCycleNumber,
|
|
11452
11616
|
title: cleanTitle,
|
|
11453
11617
|
content: logBlock,
|
|
@@ -11554,7 +11718,7 @@ ${cleanContent}`;
|
|
|
11554
11718
|
const cycleTaskCount = legacyHandoffs.length;
|
|
11555
11719
|
const cycleEffortPoints = legacyHandoffs.reduce((sum, h) => sum + (effortMap[h.handoff.effort] ?? 3), 0);
|
|
11556
11720
|
const cycleLogPromise = adapter2.writeCycleLogEntry({
|
|
11557
|
-
uuid:
|
|
11721
|
+
uuid: randomUUID8(),
|
|
11558
11722
|
cycleNumber: newCycleNumber,
|
|
11559
11723
|
title: cleanTitle,
|
|
11560
11724
|
content: logBlock,
|
|
@@ -11593,7 +11757,7 @@ ${cleanContent}`;
|
|
|
11593
11757
|
visibility = task.visibility;
|
|
11594
11758
|
}
|
|
11595
11759
|
const created = await adapter2.createTask({
|
|
11596
|
-
uuid:
|
|
11760
|
+
uuid: randomUUID8(),
|
|
11597
11761
|
displayId: "",
|
|
11598
11762
|
title: task.title,
|
|
11599
11763
|
status: task.status || "Backlog",
|
|
@@ -12921,11 +13085,11 @@ ${result.userMessage}
|
|
|
12921
13085
|
}
|
|
12922
13086
|
|
|
12923
13087
|
// src/services/strategy.ts
|
|
12924
|
-
import { randomUUID as
|
|
13088
|
+
import { randomUUID as randomUUID10, createHash as createHash2 } from "crypto";
|
|
12925
13089
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
12926
13090
|
import { existsSync, readdirSync, statSync } from "fs";
|
|
12927
|
-
import { join as
|
|
12928
|
-
import { homedir } from "os";
|
|
13091
|
+
import { join as join3 } from "path";
|
|
13092
|
+
import { homedir as homedir2 } from "os";
|
|
12929
13093
|
|
|
12930
13094
|
// src/lib/hosted-mode.ts
|
|
12931
13095
|
function isHostedTransport() {
|
|
@@ -12936,7 +13100,7 @@ function hasLocalWorkspace() {
|
|
|
12936
13100
|
}
|
|
12937
13101
|
|
|
12938
13102
|
// src/services/idea.ts
|
|
12939
|
-
import { randomUUID as
|
|
13103
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
12940
13104
|
var OWNER_ACTION_PATTERNS = [
|
|
12941
13105
|
// Verb match — the canonical owner-action verbs (spec C275 §2)
|
|
12942
13106
|
/\b(send|reply|email|call|phone|text|message|dm)\b/i,
|
|
@@ -13274,7 +13438,7 @@ ${lines.join("\n")}
|
|
|
13274
13438
|
}
|
|
13275
13439
|
const inherited = await resolveVisibilityFromDocs(adapter2, [input.docRef]);
|
|
13276
13440
|
const task = await adapter2.createTask({
|
|
13277
|
-
uuid:
|
|
13441
|
+
uuid: randomUUID9(),
|
|
13278
13442
|
displayId: "",
|
|
13279
13443
|
title: taskTitle,
|
|
13280
13444
|
status: "Backlog",
|
|
@@ -13462,6 +13626,23 @@ function generateValueReport(snapshots) {
|
|
|
13462
13626
|
|
|
13463
13627
|
// src/services/strategy.ts
|
|
13464
13628
|
var STRATEGY_DUPE_COVERAGE_THRESHOLD = 0.6;
|
|
13629
|
+
function taskStatusLabel(task) {
|
|
13630
|
+
return `[${task.status}${task.cycle != null ? ` C${task.cycle}` : ""}]`;
|
|
13631
|
+
}
|
|
13632
|
+
function annotateTaskStatuses(text, statusMap) {
|
|
13633
|
+
return text.replace(/\b(task-\d+)\b(?!\s*\[)/gi, (full, id) => {
|
|
13634
|
+
const label = statusMap.get(id.toLowerCase());
|
|
13635
|
+
return label ? `${id} ${label}` : full;
|
|
13636
|
+
});
|
|
13637
|
+
}
|
|
13638
|
+
function recActsOnTaskId(rec) {
|
|
13639
|
+
if (rec.target && /^task-\d+$/i.test(rec.target)) return rec.target;
|
|
13640
|
+
if (rec.type === "priority_change" || rec.type === "phase_transition") {
|
|
13641
|
+
const m = rec.content.match(/\btask-\d+\b/i);
|
|
13642
|
+
if (m) return m[0];
|
|
13643
|
+
}
|
|
13644
|
+
return void 0;
|
|
13645
|
+
}
|
|
13465
13646
|
async function extractRecommendations(data, cycleNumber, adapter2) {
|
|
13466
13647
|
const recs = [];
|
|
13467
13648
|
if (data.activeDecisionUpdates?.length) {
|
|
@@ -13525,6 +13706,32 @@ async function extractRecommendations(data, cycleNumber, adapter2) {
|
|
|
13525
13706
|
}
|
|
13526
13707
|
}
|
|
13527
13708
|
}
|
|
13709
|
+
if (adapter2 && recs.length > 0) {
|
|
13710
|
+
const targetIds = /* @__PURE__ */ new Set();
|
|
13711
|
+
for (const rec of recs) {
|
|
13712
|
+
const id = recActsOnTaskId(rec);
|
|
13713
|
+
if (id) targetIds.add(id.toLowerCase());
|
|
13714
|
+
}
|
|
13715
|
+
if (targetIds.size > 0) {
|
|
13716
|
+
try {
|
|
13717
|
+
const targetTasks = await adapter2.getTasks([...targetIds]);
|
|
13718
|
+
const shipped = new Set(
|
|
13719
|
+
targetTasks.filter((t) => t.status === "Done" || t.status === "Cancelled").map((t) => (t.displayId ?? t.id).toLowerCase())
|
|
13720
|
+
);
|
|
13721
|
+
if (shipped.size > 0) {
|
|
13722
|
+
return recs.filter((rec) => {
|
|
13723
|
+
const id = recActsOnTaskId(rec)?.toLowerCase();
|
|
13724
|
+
if (id && shipped.has(id)) {
|
|
13725
|
+
console.error(`[strategy_review] Dropped pending rec targeting already-shipped task ${id}: ${rec.content.slice(0, 80)}`);
|
|
13726
|
+
return false;
|
|
13727
|
+
}
|
|
13728
|
+
return true;
|
|
13729
|
+
});
|
|
13730
|
+
}
|
|
13731
|
+
} catch {
|
|
13732
|
+
}
|
|
13733
|
+
}
|
|
13734
|
+
}
|
|
13528
13735
|
return recs;
|
|
13529
13736
|
}
|
|
13530
13737
|
function classifyRecommendation(text) {
|
|
@@ -13878,11 +14085,11 @@ ${lines.join("\n")}`;
|
|
|
13878
14085
|
}
|
|
13879
14086
|
let recentPlansText;
|
|
13880
14087
|
try {
|
|
13881
|
-
const plansDir =
|
|
14088
|
+
const plansDir = join3(homedir2(), ".claude", "plans");
|
|
13882
14089
|
if (existsSync(plansDir)) {
|
|
13883
14090
|
const lastReviewDate = previousStrategyReviews?.[0]?.createdAt ? new Date(previousStrategyReviews[0].createdAt) : /* @__PURE__ */ new Date(0);
|
|
13884
14091
|
const planFiles = readdirSync(plansDir).filter((f) => f.endsWith(".md")).map((f) => {
|
|
13885
|
-
const fullPath =
|
|
14092
|
+
const fullPath = join3(plansDir, f);
|
|
13886
14093
|
const stat4 = statSync(fullPath);
|
|
13887
14094
|
return { name: f, modified: stat4.mtime, size: stat4.size };
|
|
13888
14095
|
}).filter((f) => f.modified > lastReviewDate).sort((a, b2) => b2.modified.getTime() - a.modified.getTime()).slice(0, 15);
|
|
@@ -13899,7 +14106,7 @@ ${lines.join("\n")}`;
|
|
|
13899
14106
|
}
|
|
13900
14107
|
let unregisteredDocsText;
|
|
13901
14108
|
try {
|
|
13902
|
-
const docsDir =
|
|
14109
|
+
const docsDir = join3(projectRoot, "docs");
|
|
13903
14110
|
if (hasLocalWorkspace() && existsSync(docsDir)) {
|
|
13904
14111
|
const registeredPaths = new Set(
|
|
13905
14112
|
(registeredDocs ?? []).map((d) => d.path).filter(Boolean)
|
|
@@ -13907,7 +14114,7 @@ ${lines.join("\n")}`;
|
|
|
13907
14114
|
const allDocFiles = [];
|
|
13908
14115
|
const scanDir = (dir, prefix) => {
|
|
13909
14116
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
13910
|
-
if (entry.isDirectory()) scanDir(
|
|
14117
|
+
if (entry.isDirectory()) scanDir(join3(dir, entry.name), `${prefix}${entry.name}/`);
|
|
13911
14118
|
else if (entry.name.endsWith(".md")) allDocFiles.push(`${prefix}${entry.name}`);
|
|
13912
14119
|
}
|
|
13913
14120
|
};
|
|
@@ -14043,6 +14250,38 @@ ${lines.join("\n")}`;
|
|
|
14043
14250
|
docActionStaleness: docActionStalenessText,
|
|
14044
14251
|
pendingAgendaTopics: pendingAgendaText
|
|
14045
14252
|
};
|
|
14253
|
+
try {
|
|
14254
|
+
const referencedIds = /* @__PURE__ */ new Set();
|
|
14255
|
+
const bearingFields = [
|
|
14256
|
+
context.sessionLog,
|
|
14257
|
+
context.allBuildReports,
|
|
14258
|
+
context.briefImplications,
|
|
14259
|
+
context.previousReviews,
|
|
14260
|
+
context.pendingRecommendations,
|
|
14261
|
+
context.docActionStaleness,
|
|
14262
|
+
context.taskComments
|
|
14263
|
+
];
|
|
14264
|
+
for (const field of bearingFields) {
|
|
14265
|
+
if (typeof field === "string") {
|
|
14266
|
+
for (const m of field.matchAll(/\btask-\d+\b/gi)) referencedIds.add(m[0].toLowerCase());
|
|
14267
|
+
}
|
|
14268
|
+
}
|
|
14269
|
+
if (referencedIds.size > 0) {
|
|
14270
|
+
const statusTasks = await adapter2.getTasks([...referencedIds]);
|
|
14271
|
+
const statusMap = /* @__PURE__ */ new Map();
|
|
14272
|
+
for (const t of statusTasks) statusMap.set((t.displayId ?? t.id).toLowerCase(), taskStatusLabel(t));
|
|
14273
|
+
if (statusMap.size > 0) {
|
|
14274
|
+
if (context.sessionLog) context.sessionLog = annotateTaskStatuses(context.sessionLog, statusMap);
|
|
14275
|
+
if (context.allBuildReports) context.allBuildReports = annotateTaskStatuses(context.allBuildReports, statusMap);
|
|
14276
|
+
if (context.briefImplications) context.briefImplications = annotateTaskStatuses(context.briefImplications, statusMap);
|
|
14277
|
+
if (context.previousReviews) context.previousReviews = annotateTaskStatuses(context.previousReviews, statusMap);
|
|
14278
|
+
if (context.pendingRecommendations) context.pendingRecommendations = annotateTaskStatuses(context.pendingRecommendations, statusMap);
|
|
14279
|
+
if (context.docActionStaleness) context.docActionStaleness = annotateTaskStatuses(context.docActionStaleness, statusMap);
|
|
14280
|
+
if (context.taskComments) context.taskComments = annotateTaskStatuses(context.taskComments, statusMap);
|
|
14281
|
+
}
|
|
14282
|
+
}
|
|
14283
|
+
} catch {
|
|
14284
|
+
}
|
|
14046
14285
|
const BUDGET_SOFT2 = 5e4;
|
|
14047
14286
|
const BUDGET_HARD2 = 6e4;
|
|
14048
14287
|
const compressionSteps = [];
|
|
@@ -14736,7 +14975,7 @@ async function processStrategyChangeOutput(adapter2, rawOutput, cycleNumber) {
|
|
|
14736
14975
|
|
|
14737
14976
|
${cleanContent}`;
|
|
14738
14977
|
await adapter2.writeCycleLogEntry({
|
|
14739
|
-
uuid:
|
|
14978
|
+
uuid: randomUUID10(),
|
|
14740
14979
|
cycleNumber,
|
|
14741
14980
|
title: cleanTitle,
|
|
14742
14981
|
content: logBlock
|
|
@@ -15973,15 +16212,15 @@ ${existing}` : entry;
|
|
|
15973
16212
|
|
|
15974
16213
|
// src/services/setup.ts
|
|
15975
16214
|
import { mkdir, writeFile as writeFile2, readFile as readFile4, readdir, access as access2, stat as stat2, chmod } from "fs/promises";
|
|
15976
|
-
import { join as
|
|
16215
|
+
import { join as join8, basename, extname, dirname as dirname3 } from "path";
|
|
15977
16216
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
15978
16217
|
|
|
15979
16218
|
// src/lib/detect-codebase.ts
|
|
15980
16219
|
import { existsSync as existsSync2 } from "fs";
|
|
15981
16220
|
import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
15982
|
-
import { join as
|
|
16221
|
+
import { join as join4 } from "path";
|
|
15983
16222
|
function detectCodebaseType(projectRoot) {
|
|
15984
|
-
if (existsSync2(
|
|
16223
|
+
if (existsSync2(join4(projectRoot, ".git"))) {
|
|
15985
16224
|
return "existing_codebase";
|
|
15986
16225
|
}
|
|
15987
16226
|
const manifests = [
|
|
@@ -15995,7 +16234,7 @@ function detectCodebaseType(projectRoot) {
|
|
|
15995
16234
|
"CMakeLists.txt"
|
|
15996
16235
|
];
|
|
15997
16236
|
for (const manifest of manifests) {
|
|
15998
|
-
if (existsSync2(
|
|
16237
|
+
if (existsSync2(join4(projectRoot, manifest))) {
|
|
15999
16238
|
return "existing_codebase";
|
|
16000
16239
|
}
|
|
16001
16240
|
}
|
|
@@ -16003,7 +16242,7 @@ function detectCodebaseType(projectRoot) {
|
|
|
16003
16242
|
const entries = readdirSync2(projectRoot).filter((f) => !f.startsWith("."));
|
|
16004
16243
|
const fileCount = entries.filter((f) => {
|
|
16005
16244
|
try {
|
|
16006
|
-
return statSync2(
|
|
16245
|
+
return statSync2(join4(projectRoot, f)).isFile();
|
|
16007
16246
|
} catch {
|
|
16008
16247
|
return false;
|
|
16009
16248
|
}
|
|
@@ -16015,18 +16254,18 @@ function detectCodebaseType(projectRoot) {
|
|
|
16015
16254
|
}
|
|
16016
16255
|
|
|
16017
16256
|
// src/lib/agents-bundle.ts
|
|
16018
|
-
import { readFileSync, existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
16019
|
-
import { dirname, join as
|
|
16257
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
16258
|
+
import { dirname, join as join5, resolve } from "path";
|
|
16020
16259
|
import { fileURLToPath } from "url";
|
|
16021
|
-
var PROJECT_BUNDLE_REL =
|
|
16260
|
+
var PROJECT_BUNDLE_REL = join5(".agents", "skills", "papi-cycle");
|
|
16022
16261
|
function bundleDestRel(rel) {
|
|
16023
|
-
return rel === "AGENTS.md" ? "AGENTS.md" :
|
|
16262
|
+
return rel === "AGENTS.md" ? "AGENTS.md" : join5(PROJECT_BUNDLE_REL, rel);
|
|
16024
16263
|
}
|
|
16025
16264
|
function resolveBundleDir() {
|
|
16026
16265
|
let dir = dirname(fileURLToPath(import.meta.url));
|
|
16027
16266
|
for (let i = 0; i < 5; i++) {
|
|
16028
|
-
const candidate =
|
|
16029
|
-
if (existsSync3(
|
|
16267
|
+
const candidate = join5(dir, "skills", "papi-cycle");
|
|
16268
|
+
if (existsSync3(join5(candidate, "AGENTS.md"))) return candidate;
|
|
16030
16269
|
const parent = resolve(dir, "..");
|
|
16031
16270
|
if (parent === dir) break;
|
|
16032
16271
|
dir = parent;
|
|
@@ -16038,10 +16277,10 @@ function readBundleFiles(bundleDir = resolveBundleDir()) {
|
|
|
16038
16277
|
const files = [];
|
|
16039
16278
|
const walk = (abs, rel) => {
|
|
16040
16279
|
for (const entry of readdirSync3(abs, { withFileTypes: true })) {
|
|
16041
|
-
const childAbs =
|
|
16042
|
-
const childRel = rel ?
|
|
16280
|
+
const childAbs = join5(abs, entry.name);
|
|
16281
|
+
const childRel = rel ? join5(rel, entry.name) : entry.name;
|
|
16043
16282
|
if (entry.isDirectory()) walk(childAbs, childRel);
|
|
16044
|
-
else if (entry.isFile()) files.push({ rel: childRel, content:
|
|
16283
|
+
else if (entry.isFile()) files.push({ rel: childRel, content: readFileSync2(childAbs, "utf8") });
|
|
16045
16284
|
}
|
|
16046
16285
|
};
|
|
16047
16286
|
walk(bundleDir, "");
|
|
@@ -16050,7 +16289,7 @@ function readBundleFiles(bundleDir = resolveBundleDir()) {
|
|
|
16050
16289
|
function planBundleInstall(projectRoot, projectName, opts = {}) {
|
|
16051
16290
|
const out = {};
|
|
16052
16291
|
for (const f of readBundleFiles()) {
|
|
16053
|
-
const dest =
|
|
16292
|
+
const dest = join5(projectRoot, bundleDestRel(f.rel));
|
|
16054
16293
|
if (opts.skipExisting && existsSync3(dest) && statSync3(dest).isFile()) continue;
|
|
16055
16294
|
const content = f.rel === "AGENTS.md" ? f.content.replace(/\{\{project_name\}\}/g, projectName) : f.content;
|
|
16056
16295
|
out[dest] = content;
|
|
@@ -16059,20 +16298,20 @@ function planBundleInstall(projectRoot, projectName, opts = {}) {
|
|
|
16059
16298
|
}
|
|
16060
16299
|
|
|
16061
16300
|
// src/lib/design-bundle.ts
|
|
16062
|
-
import { readFileSync as
|
|
16063
|
-
import { dirname as dirname2, join as
|
|
16301
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4, statSync as statSync4 } from "fs";
|
|
16302
|
+
import { dirname as dirname2, join as join6, resolve as resolve2 } from "path";
|
|
16064
16303
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16065
16304
|
var DESIGN_ASSETS = [
|
|
16066
|
-
{ srcRel:
|
|
16067
|
-
{ srcRel:
|
|
16068
|
-
{ srcRel:
|
|
16305
|
+
{ srcRel: join6("agents", "frontend-design-engineer.md"), destRel: join6(".claude", "agents", "frontend-design-engineer.md"), executable: false },
|
|
16306
|
+
{ srcRel: join6("skills", "design-critique", "SKILL.md"), destRel: join6(".claude", "skills", "design-critique", "SKILL.md"), executable: false },
|
|
16307
|
+
{ srcRel: join6("hooks", "frontend-design-guard.sh"), destRel: join6(".claude", "hooks", "frontend-design-guard.sh"), executable: true }
|
|
16069
16308
|
];
|
|
16070
16309
|
var DESIGN_HOOK_COMMAND = ".claude/hooks/frontend-design-guard.sh";
|
|
16071
16310
|
function resolveDesignAssetsDir() {
|
|
16072
16311
|
let dir = dirname2(fileURLToPath2(import.meta.url));
|
|
16073
16312
|
for (let i = 0; i < 5; i++) {
|
|
16074
|
-
const candidate =
|
|
16075
|
-
if (existsSync4(
|
|
16313
|
+
const candidate = join6(dir, "design-assets");
|
|
16314
|
+
if (existsSync4(join6(candidate, "agents", "frontend-design-engineer.md"))) return candidate;
|
|
16076
16315
|
const parent = resolve2(dir, "..");
|
|
16077
16316
|
if (parent === dir) break;
|
|
16078
16317
|
dir = parent;
|
|
@@ -16084,23 +16323,23 @@ function planDesignInstall(projectRoot, opts = {}) {
|
|
|
16084
16323
|
if (!assetsDir) return [];
|
|
16085
16324
|
const out = [];
|
|
16086
16325
|
for (const asset of DESIGN_ASSETS) {
|
|
16087
|
-
const srcAbs =
|
|
16326
|
+
const srcAbs = join6(assetsDir, asset.srcRel);
|
|
16088
16327
|
if (!existsSync4(srcAbs)) continue;
|
|
16089
|
-
const dest = projectRoot ?
|
|
16328
|
+
const dest = projectRoot ? join6(projectRoot, asset.destRel) : asset.destRel;
|
|
16090
16329
|
if (opts.skipExisting && projectRoot && existsSync4(dest) && statSync4(dest).isFile()) continue;
|
|
16091
|
-
out.push({ dest, content:
|
|
16330
|
+
out.push({ dest, content: readFileSync3(srcAbs, "utf8"), executable: asset.executable });
|
|
16092
16331
|
}
|
|
16093
16332
|
return out;
|
|
16094
16333
|
}
|
|
16095
16334
|
|
|
16096
16335
|
// src/lib/skill-detection.ts
|
|
16097
|
-
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as
|
|
16098
|
-
import { join as
|
|
16336
|
+
import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
|
|
16337
|
+
import { join as join7 } from "path";
|
|
16099
16338
|
function readPackageJson(projectRoot) {
|
|
16100
|
-
const path7 =
|
|
16339
|
+
const path7 = join7(projectRoot, "package.json");
|
|
16101
16340
|
if (!existsSync5(path7)) return null;
|
|
16102
16341
|
try {
|
|
16103
|
-
const raw =
|
|
16342
|
+
const raw = readFileSync4(path7, "utf-8");
|
|
16104
16343
|
return JSON.parse(raw);
|
|
16105
16344
|
} catch {
|
|
16106
16345
|
return null;
|
|
@@ -16121,7 +16360,7 @@ function detectsFrontendStack(projectRoot) {
|
|
|
16121
16360
|
return hasDependencyMatching(allDeps(readPackageJson(projectRoot)), FRONTEND_DEP_PATTERN);
|
|
16122
16361
|
}
|
|
16123
16362
|
function hasGitHubWorkflows(projectRoot) {
|
|
16124
|
-
const dir =
|
|
16363
|
+
const dir = join7(projectRoot, ".github", "workflows");
|
|
16125
16364
|
if (!existsSync5(dir)) return false;
|
|
16126
16365
|
try {
|
|
16127
16366
|
const entries = readdirSync4(dir);
|
|
@@ -16131,18 +16370,18 @@ function hasGitHubWorkflows(projectRoot) {
|
|
|
16131
16370
|
}
|
|
16132
16371
|
}
|
|
16133
16372
|
function envExampleMentionsStaging(projectRoot) {
|
|
16134
|
-
const path7 =
|
|
16373
|
+
const path7 = join7(projectRoot, ".env.example");
|
|
16135
16374
|
if (!existsSync5(path7)) return false;
|
|
16136
16375
|
try {
|
|
16137
|
-
const raw =
|
|
16376
|
+
const raw = readFileSync4(path7, "utf-8");
|
|
16138
16377
|
return /\b(STAGING_URL|STAGING_API|STAGING_HOST|NEXT_PUBLIC_STAGING)/i.test(raw);
|
|
16139
16378
|
} catch {
|
|
16140
16379
|
return false;
|
|
16141
16380
|
}
|
|
16142
16381
|
}
|
|
16143
16382
|
function hasVercelConfig(projectRoot) {
|
|
16144
|
-
if (existsSync5(
|
|
16145
|
-
const vercelDir =
|
|
16383
|
+
if (existsSync5(join7(projectRoot, "vercel.json"))) return true;
|
|
16384
|
+
const vercelDir = join7(projectRoot, ".vercel");
|
|
16146
16385
|
if (!existsSync5(vercelDir)) return false;
|
|
16147
16386
|
try {
|
|
16148
16387
|
return statSync5(vercelDir).isDirectory();
|
|
@@ -16631,7 +16870,7 @@ async function scaffoldPapiDir(adapter2, config2, input, collector) {
|
|
|
16631
16870
|
await mkdir(config2.papiDir, { recursive: true });
|
|
16632
16871
|
for (const [filename, template] of Object.entries(FILE_TEMPLATES)) {
|
|
16633
16872
|
const content = substitute(template, vars);
|
|
16634
|
-
await writeFile2(
|
|
16873
|
+
await writeFile2(join8(config2.papiDir, filename), content, "utf-8");
|
|
16635
16874
|
}
|
|
16636
16875
|
}
|
|
16637
16876
|
} else {
|
|
@@ -16649,13 +16888,13 @@ async function scaffoldPapiDir(adapter2, config2, input, collector) {
|
|
|
16649
16888
|
const useCollector = config2.adapterType === "proxy";
|
|
16650
16889
|
const docsRel = "docs";
|
|
16651
16890
|
const commandsRel = ".claude/commands";
|
|
16652
|
-
const commandsDir = useCollector ? commandsRel :
|
|
16653
|
-
const docsDir = useCollector ? docsRel :
|
|
16891
|
+
const commandsDir = useCollector ? commandsRel : join8(config2.projectRoot, ".claude", "commands");
|
|
16892
|
+
const docsDir = useCollector ? docsRel : join8(config2.projectRoot, "docs");
|
|
16654
16893
|
if (!useCollector) {
|
|
16655
16894
|
await mkdir(commandsDir, { recursive: true });
|
|
16656
16895
|
await mkdir(docsDir, { recursive: true });
|
|
16657
16896
|
}
|
|
16658
|
-
const claudeMdPath = useCollector ? "CLAUDE.md" :
|
|
16897
|
+
const claudeMdPath = useCollector ? "CLAUDE.md" : join8(config2.projectRoot, "CLAUDE.md");
|
|
16659
16898
|
let claudeMdExists = false;
|
|
16660
16899
|
if (!useCollector) {
|
|
16661
16900
|
try {
|
|
@@ -16664,7 +16903,7 @@ async function scaffoldPapiDir(adapter2, config2, input, collector) {
|
|
|
16664
16903
|
} catch {
|
|
16665
16904
|
}
|
|
16666
16905
|
}
|
|
16667
|
-
const docsIndexPath = useCollector ? `${docsRel}/INDEX.md` :
|
|
16906
|
+
const docsIndexPath = useCollector ? `${docsRel}/INDEX.md` : join8(docsDir, "INDEX.md");
|
|
16668
16907
|
let docsIndexExists = false;
|
|
16669
16908
|
if (!useCollector) {
|
|
16670
16909
|
try {
|
|
@@ -16674,9 +16913,9 @@ async function scaffoldPapiDir(adapter2, config2, input, collector) {
|
|
|
16674
16913
|
}
|
|
16675
16914
|
}
|
|
16676
16915
|
const scaffoldFiles = {
|
|
16677
|
-
[useCollector ? `${commandsRel}/papi-audit.md` :
|
|
16678
|
-
[useCollector ? `${commandsRel}/test.md` :
|
|
16679
|
-
[useCollector ? `${docsRel}/README.md` :
|
|
16916
|
+
[useCollector ? `${commandsRel}/papi-audit.md` : join8(commandsDir, "papi-audit.md")]: PAPI_AUDIT_COMMAND_TEMPLATE,
|
|
16917
|
+
[useCollector ? `${commandsRel}/test.md` : join8(commandsDir, "test.md")]: TEST_COMMAND_TEMPLATE,
|
|
16918
|
+
[useCollector ? `${docsRel}/README.md` : join8(docsDir, "README.md")]: substitute(DOCS_README_TEMPLATE, vars)
|
|
16680
16919
|
};
|
|
16681
16920
|
if (!docsIndexExists) {
|
|
16682
16921
|
scaffoldFiles[docsIndexPath] = substitute(DOCS_INDEX_TEMPLATE, vars);
|
|
@@ -16703,7 +16942,7 @@ async function scaffoldPapiDir(adapter2, config2, input, collector) {
|
|
|
16703
16942
|
if (useCollector) {
|
|
16704
16943
|
scaffoldFiles[".cursor/rules/papi.mdc"] = substitute(CURSOR_RULES_TEMPLATE, vars);
|
|
16705
16944
|
} else {
|
|
16706
|
-
const cursorDir =
|
|
16945
|
+
const cursorDir = join8(config2.projectRoot, ".cursor");
|
|
16707
16946
|
let cursorDetected = false;
|
|
16708
16947
|
try {
|
|
16709
16948
|
await access2(cursorDir);
|
|
@@ -16711,8 +16950,8 @@ async function scaffoldPapiDir(adapter2, config2, input, collector) {
|
|
|
16711
16950
|
} catch {
|
|
16712
16951
|
}
|
|
16713
16952
|
if (cursorDetected) {
|
|
16714
|
-
const cursorRulesDir =
|
|
16715
|
-
const cursorRulesPath =
|
|
16953
|
+
const cursorRulesDir = join8(cursorDir, "rules");
|
|
16954
|
+
const cursorRulesPath = join8(cursorRulesDir, "papi.mdc");
|
|
16716
16955
|
await mkdir(cursorRulesDir, { recursive: true });
|
|
16717
16956
|
try {
|
|
16718
16957
|
await access2(cursorRulesPath);
|
|
@@ -16767,7 +17006,7 @@ async function scaffoldPapiDir(adapter2, config2, input, collector) {
|
|
|
16767
17006
|
}
|
|
16768
17007
|
var PAPI_PERMISSION = "mcp__papi__*";
|
|
16769
17008
|
async function ensurePapiPermission(projectRoot) {
|
|
16770
|
-
const settingsPath =
|
|
17009
|
+
const settingsPath = join8(projectRoot, ".claude", "settings.json");
|
|
16771
17010
|
try {
|
|
16772
17011
|
let settings = {};
|
|
16773
17012
|
try {
|
|
@@ -16786,13 +17025,13 @@ async function ensurePapiPermission(projectRoot) {
|
|
|
16786
17025
|
if (!allow.includes(PAPI_PERMISSION)) {
|
|
16787
17026
|
allow.push(PAPI_PERMISSION);
|
|
16788
17027
|
}
|
|
16789
|
-
await mkdir(
|
|
17028
|
+
await mkdir(join8(projectRoot, ".claude"), { recursive: true });
|
|
16790
17029
|
await writeFile2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
16791
17030
|
} catch {
|
|
16792
17031
|
}
|
|
16793
17032
|
}
|
|
16794
17033
|
async function ensureDesignHookRegistered(projectRoot) {
|
|
16795
|
-
const settingsPath =
|
|
17034
|
+
const settingsPath = join8(projectRoot, ".claude", "settings.json");
|
|
16796
17035
|
try {
|
|
16797
17036
|
let settings = {};
|
|
16798
17037
|
try {
|
|
@@ -16822,7 +17061,7 @@ async function ensureDesignHookRegistered(projectRoot) {
|
|
|
16822
17061
|
chain.push({ type: "command", command: DESIGN_HOOK_COMMAND });
|
|
16823
17062
|
}
|
|
16824
17063
|
}
|
|
16825
|
-
await mkdir(
|
|
17064
|
+
await mkdir(join8(projectRoot, ".claude"), { recursive: true });
|
|
16826
17065
|
await writeFile2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
16827
17066
|
} catch {
|
|
16828
17067
|
}
|
|
@@ -16926,7 +17165,7 @@ ${conventionsText.trim()}
|
|
|
16926
17165
|
);
|
|
16927
17166
|
} else {
|
|
16928
17167
|
try {
|
|
16929
|
-
const claudeMdPath =
|
|
17168
|
+
const claudeMdPath = join8(config2.projectRoot, "CLAUDE.md");
|
|
16930
17169
|
const existing = await readFile4(claudeMdPath, "utf-8");
|
|
16931
17170
|
if (existing.includes(CONVENTIONS_SENTINEL) || existing.includes(CONVENTIONS_HEADING)) {
|
|
16932
17171
|
warnings.push(
|
|
@@ -17013,13 +17252,13 @@ async function scanCodebase(projectRoot) {
|
|
|
17013
17252
|
}
|
|
17014
17253
|
let packageJson;
|
|
17015
17254
|
try {
|
|
17016
|
-
const content = await readFile4(
|
|
17255
|
+
const content = await readFile4(join8(projectRoot, "package.json"), "utf-8");
|
|
17017
17256
|
packageJson = JSON.parse(content);
|
|
17018
17257
|
} catch {
|
|
17019
17258
|
}
|
|
17020
17259
|
let readme;
|
|
17021
17260
|
for (const name of ["README.md", "readme.md", "README.txt", "README"]) {
|
|
17022
|
-
const content = await safeReadFile(
|
|
17261
|
+
const content = await safeReadFile(join8(projectRoot, name), 5e3);
|
|
17023
17262
|
if (content) {
|
|
17024
17263
|
readme = content;
|
|
17025
17264
|
break;
|
|
@@ -17029,7 +17268,7 @@ async function scanCodebase(projectRoot) {
|
|
|
17029
17268
|
let totalFiles = topLevelFiles.length;
|
|
17030
17269
|
for (const dir of topLevelDirs) {
|
|
17031
17270
|
try {
|
|
17032
|
-
const entries = await readdir(
|
|
17271
|
+
const entries = await readdir(join8(projectRoot, dir), { withFileTypes: true });
|
|
17033
17272
|
const files = entries.filter((e) => e.isFile());
|
|
17034
17273
|
const extensions = [...new Set(files.map((f) => extname(f.name).toLowerCase()).filter(Boolean))];
|
|
17035
17274
|
totalFiles += files.length;
|
|
@@ -17376,7 +17615,7 @@ async function applySetup(adapter2, config2, input, briefText, adSeedText, conve
|
|
|
17376
17615
|
collector.add({ path: "CLAUDE.md", content: dogfoodSection, mode: "append" });
|
|
17377
17616
|
} else {
|
|
17378
17617
|
try {
|
|
17379
|
-
const claudeMdPath =
|
|
17618
|
+
const claudeMdPath = join8(config2.projectRoot, "CLAUDE.md");
|
|
17380
17619
|
const existing = await readFile4(claudeMdPath, "utf-8");
|
|
17381
17620
|
if (!existing.includes("Dogfood Logging")) {
|
|
17382
17621
|
await writeFile2(claudeMdPath, existing + dogfoodSection, "utf-8");
|
|
@@ -17421,7 +17660,7 @@ async function applySetup(adapter2, config2, input, briefText, adSeedText, conve
|
|
|
17421
17660
|
cursorScaffolded = true;
|
|
17422
17661
|
} else {
|
|
17423
17662
|
try {
|
|
17424
|
-
await access2(
|
|
17663
|
+
await access2(join8(config2.projectRoot, ".cursor", "rules", "papi.mdc"));
|
|
17425
17664
|
cursorScaffolded = true;
|
|
17426
17665
|
} catch {
|
|
17427
17666
|
}
|
|
@@ -17441,11 +17680,11 @@ async function applySetup(adapter2, config2, input, briefText, adSeedText, conve
|
|
|
17441
17680
|
}
|
|
17442
17681
|
async function ensureMcpJsonGitignored(projectRoot) {
|
|
17443
17682
|
try {
|
|
17444
|
-
await access2(
|
|
17683
|
+
await access2(join8(projectRoot, ".git"));
|
|
17445
17684
|
} catch {
|
|
17446
17685
|
return void 0;
|
|
17447
17686
|
}
|
|
17448
|
-
const gitignorePath =
|
|
17687
|
+
const gitignorePath = join8(projectRoot, ".gitignore");
|
|
17449
17688
|
let existing = "";
|
|
17450
17689
|
try {
|
|
17451
17690
|
existing = await readFile4(gitignorePath, "utf-8");
|
|
@@ -17955,131 +18194,10 @@ init_git();
|
|
|
17955
18194
|
init_git();
|
|
17956
18195
|
|
|
17957
18196
|
// src/services/release.ts
|
|
18197
|
+
init_telemetry();
|
|
17958
18198
|
import { writeFile as writeFile3, readFile as readFile5 } from "fs/promises";
|
|
17959
18199
|
import { join as join9 } from "path";
|
|
17960
18200
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
17961
|
-
|
|
17962
|
-
// src/lib/install-id.ts
|
|
17963
|
-
import { randomUUID as randomUUID10 } from "crypto";
|
|
17964
|
-
import { mkdirSync, readFileSync as readFileSync4, writeFileSync, chmodSync } from "fs";
|
|
17965
|
-
import { homedir as homedir2 } from "os";
|
|
17966
|
-
import { join as join8 } from "path";
|
|
17967
|
-
var PAPI_HOME_DIR = join8(homedir2(), ".papi");
|
|
17968
|
-
var INSTALL_ID_FILE = join8(PAPI_HOME_DIR, "install-id.json");
|
|
17969
|
-
var cachedInstallId = null;
|
|
17970
|
-
function isValidUuid(s) {
|
|
17971
|
-
return typeof s === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);
|
|
17972
|
-
}
|
|
17973
|
-
function getInstallId() {
|
|
17974
|
-
if (cachedInstallId) return cachedInstallId;
|
|
17975
|
-
try {
|
|
17976
|
-
const raw = readFileSync4(INSTALL_ID_FILE, "utf-8");
|
|
17977
|
-
const parsed = JSON.parse(raw);
|
|
17978
|
-
if (isValidUuid(parsed.install_id)) {
|
|
17979
|
-
cachedInstallId = parsed.install_id;
|
|
17980
|
-
return cachedInstallId;
|
|
17981
|
-
}
|
|
17982
|
-
} catch {
|
|
17983
|
-
}
|
|
17984
|
-
try {
|
|
17985
|
-
mkdirSync(PAPI_HOME_DIR, { recursive: true, mode: 448 });
|
|
17986
|
-
const id = randomUUID10();
|
|
17987
|
-
const contents = {
|
|
17988
|
-
install_id: id,
|
|
17989
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
17990
|
-
};
|
|
17991
|
-
writeFileSync(INSTALL_ID_FILE, JSON.stringify(contents, null, 2), { mode: 384 });
|
|
17992
|
-
try {
|
|
17993
|
-
chmodSync(INSTALL_ID_FILE, 384);
|
|
17994
|
-
} catch {
|
|
17995
|
-
}
|
|
17996
|
-
cachedInstallId = id;
|
|
17997
|
-
return cachedInstallId;
|
|
17998
|
-
} catch {
|
|
17999
|
-
return null;
|
|
18000
|
-
}
|
|
18001
|
-
}
|
|
18002
|
-
|
|
18003
|
-
// src/lib/telemetry.ts
|
|
18004
|
-
var HOSTED_SUPABASE_URL2 = process.env["PAPI_HOSTED_SUPABASE_URL"] ?? "https://guewgygcpcmrcoppihzx.supabase.co";
|
|
18005
|
-
var DEFAULT_TELEMETRY_ENDPOINT = `${HOSTED_SUPABASE_URL2}/functions/v1/data-proxy`;
|
|
18006
|
-
var MD_PINGS_SUPABASE_URL = process.env["PAPI_MD_PINGS_URL"] ?? HOSTED_SUPABASE_URL2;
|
|
18007
|
-
var MD_PINGS_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd1ZXdneWdjcGNtcmNvcHBpaHp4Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzI2Njk2NTMsImV4cCI6MjA4ODI0NTY1M30.V5Jw7wJgiMpSQPa2mt0ftjyye5ynG1qLlam00yPVNJY";
|
|
18008
|
-
function isEnabled() {
|
|
18009
|
-
const val = process.env["PAPI_TELEMETRY"];
|
|
18010
|
-
return val !== "false" && val !== "off";
|
|
18011
|
-
}
|
|
18012
|
-
function emitTelemetryEvent(event) {
|
|
18013
|
-
if (!isEnabled()) return;
|
|
18014
|
-
const apiKey = process.env["PAPI_DATA_API_KEY"];
|
|
18015
|
-
if (!apiKey) return;
|
|
18016
|
-
const endpoint = process.env["PAPI_DATA_ENDPOINT"] ?? DEFAULT_TELEMETRY_ENDPOINT;
|
|
18017
|
-
const body = {
|
|
18018
|
-
projectId: event.project_id,
|
|
18019
|
-
toolName: event.tool_name,
|
|
18020
|
-
eventType: event.event_type,
|
|
18021
|
-
metadata: event.metadata ?? {}
|
|
18022
|
-
};
|
|
18023
|
-
fetch(`${endpoint}/telemetry`, {
|
|
18024
|
-
method: "POST",
|
|
18025
|
-
headers: {
|
|
18026
|
-
"Content-Type": "application/json",
|
|
18027
|
-
"Authorization": `Bearer ${apiKey}`
|
|
18028
|
-
},
|
|
18029
|
-
body: JSON.stringify(body),
|
|
18030
|
-
signal: AbortSignal.timeout(5e3)
|
|
18031
|
-
}).catch(() => {
|
|
18032
|
-
});
|
|
18033
|
-
}
|
|
18034
|
-
function emitToolCall(projectId, toolName, durationMs, extra) {
|
|
18035
|
-
emitTelemetryEvent({
|
|
18036
|
-
project_id: projectId,
|
|
18037
|
-
tool_name: toolName,
|
|
18038
|
-
event_type: "tool_call",
|
|
18039
|
-
metadata: { duration_ms: durationMs, ...extra }
|
|
18040
|
-
});
|
|
18041
|
-
}
|
|
18042
|
-
function emitMdAdapterPing(toolName, extra, userId, projectSlug) {
|
|
18043
|
-
if (!isEnabled()) return;
|
|
18044
|
-
const installId = getInstallId();
|
|
18045
|
-
if (!installId) return;
|
|
18046
|
-
const resolvedUserId = userId ?? process.env["PAPI_USER_ID"] ?? void 0;
|
|
18047
|
-
const body = {
|
|
18048
|
-
install_id: installId,
|
|
18049
|
-
tool_name: toolName,
|
|
18050
|
-
papi_version: process.env["npm_package_version"] ?? null,
|
|
18051
|
-
metadata: extra ?? {}
|
|
18052
|
-
};
|
|
18053
|
-
if (resolvedUserId) body["user_id"] = resolvedUserId;
|
|
18054
|
-
if (projectSlug) body["project_slug"] = projectSlug;
|
|
18055
|
-
fetch(`${MD_PINGS_SUPABASE_URL}/rest/v1/md_adapter_pings`, {
|
|
18056
|
-
method: "POST",
|
|
18057
|
-
headers: {
|
|
18058
|
-
"Content-Type": "application/json",
|
|
18059
|
-
"apikey": MD_PINGS_ANON_KEY,
|
|
18060
|
-
"Authorization": `Bearer ${MD_PINGS_ANON_KEY}`,
|
|
18061
|
-
"Prefer": "return=minimal"
|
|
18062
|
-
},
|
|
18063
|
-
body: JSON.stringify(body),
|
|
18064
|
-
signal: AbortSignal.timeout(5e3)
|
|
18065
|
-
}).catch(() => {
|
|
18066
|
-
});
|
|
18067
|
-
}
|
|
18068
|
-
function emitMilestone(projectId, milestone, extra) {
|
|
18069
|
-
emitTelemetryEvent({
|
|
18070
|
-
project_id: projectId,
|
|
18071
|
-
tool_name: milestone,
|
|
18072
|
-
event_type: "milestone",
|
|
18073
|
-
metadata: extra
|
|
18074
|
-
});
|
|
18075
|
-
}
|
|
18076
|
-
function resolveTelemetryProjectId(config2) {
|
|
18077
|
-
if (config2.projectId && config2.projectId.length > 0) return config2.projectId;
|
|
18078
|
-
const env = process.env["PAPI_PROJECT_ID"];
|
|
18079
|
-
return env && env.length > 0 ? env : void 0;
|
|
18080
|
-
}
|
|
18081
|
-
|
|
18082
|
-
// src/services/release.ts
|
|
18083
18201
|
init_git();
|
|
18084
18202
|
var INITIAL_RELEASE_NOTES = `# Changelog
|
|
18085
18203
|
|
|
@@ -27099,6 +27217,9 @@ ${serializeBuildHandoff(task.buildHandoff)}`);
|
|
|
27099
27217
|
}
|
|
27100
27218
|
}
|
|
27101
27219
|
|
|
27220
|
+
// src/server.ts
|
|
27221
|
+
init_telemetry();
|
|
27222
|
+
|
|
27102
27223
|
// src/services/metering.ts
|
|
27103
27224
|
var TIER_ALLOWANCES = {
|
|
27104
27225
|
free: 1e3,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papi-ai/server",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.42",
|
|
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.cathalos92/papi",
|